1
0
Fork 0

Initial commit

Signed-off-by: Julien Riou <julien@riou.xyz>
This commit is contained in:
Julien Riou 2024-12-22 07:56:14 +01:00 committed by Julien Riou
commit 8e018ba84d
Signed by: jriou
GPG key ID: 9A099EDA51316854
43 changed files with 14239 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
public/
*.odt
.hugo_build.lock

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "themes/hugo-theme-console"]
path = themes/hugo-theme-console
url = https://github.com/mrmierzejewski/hugo-theme-console.git

156
LICENSE Normal file
View file

@ -0,0 +1,156 @@
Creative Commons Attribution 4.0 International
Creative Commons Corporation (“Creative Commons”) is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an “as-is” basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible.
Using Creative Commons Public Licenses
Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses.
Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC-licensed material, or material used under an exception or limitation to copyright. More considerations for licensors.
Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensors permission is not necessary for any reasonfor example, because of any applicable exception or limitation to copyrightthen that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More considerations for the public.
Creative Commons Attribution 4.0 International Public License
By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.
Section 1 Definitions.
a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image.
b. Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License.
c. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights.
d. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements.
e. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material.
f. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License.
g. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license.
h. Licensor means the individual(s) or entity(ies) granting rights under this Public License.
i. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them.
j. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world.
k. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning.
Section 2 Scope.
a. License grant.
1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to:
A. reproduce and Share the Licensed Material, in whole or in part; and
B. produce, reproduce, and Share Adapted Material.
2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions.
3. Term. The term of this Public License is specified in Section 6(a).
4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material.
5. Downstream recipients.
A. Offer from the Licensor Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License.
B. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material.
6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i).
b. Other rights.
1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise.
2. Patent and trademark rights are not licensed under this Public License.
3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties.
Section 3 License Conditions.
Your exercise of the Licensed Rights is expressly made subject to the following conditions.
a. Attribution.
1. If You Share the Licensed Material (including in modified form), You must:
A. retain the following if it is supplied by the Licensor with the Licensed Material:
i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated);
ii. a copyright notice;
iii. a notice that refers to this Public License;
iv. a notice that refers to the disclaimer of warranties;
v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable;
B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and
C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License.
2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information.
3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable.
4. If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License.
Section 4 Sui Generis Database Rights.
Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material:
a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database;
b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and
c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database.
For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights.
Section 5 Disclaimer of Warranties and Limitation of Liability.
a. Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.
b. To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.
c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability.
Section 6 Term and Termination.
a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically.
b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates:
1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or
2. upon express reinstatement by the Licensor.
c. For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License.
d. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License.
e. Sections 1, 5, 6, 7, and 8 survive termination of this Public License.
Section 7 Other Terms and Conditions.
a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed.
b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License.
Section 8 Interpretation.
a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License.
b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions.
c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor.
d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority.
Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark “Creative Commons” or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses.
Creative Commons may be contacted at creativecommons.org.

5
README.md Normal file
View file

@ -0,0 +1,5 @@
# self-hosting.riou.xyz
Source code behind [self-hosting.riou.xyz](https://self-hosting.riou.xyz) blog.
Made with [Hugo](https://gohugo.io/) and
[Console](https://github.com/mrmierzejewski/hugo-theme-console) theme.

6
archetypes/default.md Normal file
View file

@ -0,0 +1,6 @@
---
title: "{{ replace .Name "-" " " | title }}"
date: {{ .Date }}
draft: true
---

5
config.toml Normal file
View file

@ -0,0 +1,5 @@
baseURL = "https://self-hosting.riou.xyz/"
languageCode = "en-us"
title = "self-hosting"
theme = "hugo-theme-console"
enableRobotsTXT = true

View file

@ -0,0 +1,136 @@
---
title: "Do your sensors yourself"
date: 2020-08-17T18:00:00+02:00
---
A big question I've asked myself during this project is what is the best place to put my storage servers? There are
multiple environmental variables to watch out: **temperature**, **humidity** and **noise**. If components are too hot,
they could be damaged in the long run. Of course, water and electricity are not friends. You can add a fan to move air
out of the case and reduce both temperature and humidity but the computer will become noisy. We need to measure those
variables. Unfortunately, all systems have different set of built-in sensors but not all of them are exposed to the
operating system. So I decided to build my own sensors.
# Sensors hardware
I'm a newbie in electronics. I never weld anything. In the DIY[^1] world, there is a open-source micro-controller, the
[Arduino Uno](https://store.arduino.cc/arduino-uno-rev3), that costs only a few bucks (20€). There are cheaper
alternatives available like the Elegoo Uno (11€). To build sensors, you'll need good sensors like the
[DHT22](https://www.waveshare.com/wiki/DHT22_Temperature-Humidity_Sensor) for temperature and humidity and
[KY-037](https://electropeak.com/learn/how-to-use-ky-037-sound-detection-sensor-with-arduino/) for capturing sound. To
connect everything together, you'll need a [breadboard](https://en.wikipedia.org/wiki/Breadboard),
[resistors](https://en.wikipedia.org/wiki/Resistor) and cables.
Components:
- [Elegoo Uno R3](https://www.amazon.fr/dp/B01N91PVIS/ref=cm_sw_r_tw_dp_x_8NtkFbHZ6X6K9)
- [DHT22 sensor](https://www.amazon.fr/dp/B07TTJNY1C/ref=cm_sw_r_tw_dp_x_QOtkFbBM2ZAAD)
- [KY-037 sensor](https://www.amazon.fr/dp/B07ZHGX5T6/ref=cm_sw_r_tw_dp_x_kPtkFbXRRK7ZP)
- [10k Ω resistor](https://www.amazon.fr/dp/B06XKQLPFV/ref=cm_sw_r_tw_dp_x_EPtkFbB24855X)
- [breadboard](https://www.amazon.fr/dp/B06XKZWCJB/ref=cm_sw_r_tw_dp_x_.PtkFb01X4WNW)
- [cables](https://www.amazon.fr/dp/B01JD5WCG2/ref=cm_sw_r_tw_dp_x_QQtkFbRA6PSG0)
In electronics, you need to build closed circuits going from the power supply ("+") to the ground ("-"). The Arduino
card can be plugged on an USB port which provides power to the card, on the "5V" pin. The end of the circuit should
return to the "GND" pin, which means "ground". The breadboard can help you extending the circuit and plug more than one
element (resistors and sensors at the same time). The top and bottom parts are connected horizontally. The central part
connects elements vertically. Horizontal and vertical parts are isolated from each other. Resistors role is to regulate
electrical intensity. They act like a tap for distributing water. If there is too much water at a time, the glass can be
full too quickly and water can spit everywhere. We'll put a resistor in front of the DHT22 to have valid values and to
prevent damages.
The circuit looks like this:
{{< rawhtml >}}
<p style="text-align: center;"><img src="/sensors.svg" alt="Sensors circuit" style="width: 65%;"></p>
{{< /rawhtml >}}
The DHT22 sensor has three pins: **power**, **digital** and **ground** (and not four like in the schema). The KY-037
sensor has four pins: **analog**, **ground**, **power** and **digital** (and not three like in the schema). We'll use
the analog pin to gather data from the sound sensor.
# Sensors software
The circuit is plugged to a computer via USB and it's ready to be used. To be able to read values, we need to compile
low-level code and execute it on the board. For this purpose, you can install the [Arduino
IDE](https://www.arduino.cc/en/Main/Software) which is available on multiple platforms. My personal computer runs on
Ubuntu (no joke please) and I tried to use the packages from the repositories. However, they are too old to work. You
should [install the IDE yourself](https://www.arduino.cc/en/Guide/Linux). I've added my own user to the "dialout" group
to be able to use the serial interface to send compiled code to the board. The code itself is called a "sketch". You can
find mine [here](https://github.com/jouir/arduino-sensors-toolkit/blob/master/sensors2serial.ino). Click on "Upload",
job done.
# Multiplexing
Values are sent to the serial port but only one program can read this interface at a time. No luck, we would like to
send those metrics to the alerting and trending systems. Both have their own schedules. They will try to access this
interface at the same time. Moreover, programs that would like to read the serial port will have to wait for, at least,
four seconds. In the IoT[^2] world, we often see the usage of [MQTT](https://en.wikipedia.org/wiki/MQTT), a queuing
protocol. To solve the performance issue, I've developed a simple daemon that reads values from the serial interface and
publishes them to an MQTT broker called [serial2mqtt](https://github.com/jouir/arduino-sensors-toolkit/#serial2mqtt).
I've installed [Mosquitto](https://mosquitto.org/) on storage servers so the multiplexing happens locally.
# Thresholds
What is the **critical temperature**? I [found](https://www.apc.com/us/en/faqs/FA157464/) that UPS batteries should not
run in an environment where temperatures exceed 25°C (warning) and must not go over 40°C (critical). This summer, I had
multiple buzzer alerts on storage3 and the temperature was over 29°C every time.
What is the **critical humidity**? Humidity is the concentration of water in a volume of air. In tropical regions of the
world, we often see a 100% humidity level, with working computers. Humidity is proportional to the temperature. The
hotter it is, the more water can be contained in the air. Generally, temperature in a computer case is warmer than the
ambient temperature. What is dangerous is not the quantity of water in the air, it's when water condense. A good rule of
thumb is to avoid going over 80%. But 100% should not be a problem.
# Alerting
On Nagios, I use the [check-mqtt](https://github.com/jpmens/check-mqtt) script on the monitored storage host under an
NRPE command:
```
# Sensors
command[check_ambient_temperature]=/usr/local/bin/python3.7 /usr/local/libexec/nagios/check-mqtt.py -m 10 --readonly -t sensors/temperature -H localhost -P 1883 -u nagios -p ***** -w "float(payload) > 25.0" -c "float(payload) > 40.0"
command[check_ambient_humidity]=/usr/local/bin/python3.7 /usr/local/libexec/nagios/check-mqtt.py -m 10 --readonly -t sensors/humidity -H localhost -P 1883 -u nagios -p ***** -w "float(payload) > 80.0" -c "float(payload) > 95.0"
```
[![storage2](/sensors-storage2-alert.png)](/sensors-storage2-alert.png)
# Observability
Telegraf has a [mqtt_consumer](https://github.com/influxdata/telegraf/tree/master/plugins/inputs/mqtt_consumer) input
plugin:
```
[[inputs.mqtt_consumer]]
servers = ["tcp://localhost:1883"]
topics = [
"sensors/humidity",
"sensors/temperature",
"sensors/sound"
]
persistent_session = true
client_id = "telegraf"
data_format = "value"
data_type = "float"
username = "telegraf"
password = "*****"
```
Grafana is able to display environmental variables now:
[![storage1](/sensors-storage1.png)](/sensors-storage1.png)
[![storage2](/sensors-storage2.png)](/sensors-storage2.png)
[![storage3](/sensors-storage3.png)](/sensors-storage3.png)
# In the end
I tried to measure noise but I failed. The KY-037 sensor is designed to detect sound variations like a big noise for a
short period of time. When we try to measure the ambient noise level, it requires a lot of conversions to get values in
[decibel](https://en.wikipedia.org/wiki/Decibel). So I decided to ignore values coming from the sensor and to hear it
myself.
I can put my storage servers in the attic, in a room or in the cellar. The attic is right under the roof which is too
hot in the summer (over 40°C). Rooms are occupied during the night and noise is a problem. I am lucky to have a free
room right now but it's too hot during the summer (over 25°C). There is the cellar left, where all the conditions are
optimal, even humidity. Remote locations all have a cellar which is perfect!
[^1]: Do It Yourself
[^2]: Internet of Things

View file

@ -0,0 +1,136 @@
---
title: "Geographic distribution with Sanoid and Syncoid"
date: 2020-08-03T18:00:00+02:00
---
Failures happen at multiple levels: a single disk can fail, as well as multiple disks, a single server, multiple
servers, a geographic region, a country, the world, the universe. The probability decreases with the number of
simultaneous events. Costs and complexity increase with the number of failure events you want to handle. It's up to you
to find the right balance between all those variables.
For my own infrastructure at home, I was able to put storage servers into three different locations. Two in Belgium
(with 10Km distance from one another), one in France. They all share the same data. Up to two storage servers can burn
or be flooded entirely without data loss. There are different redundant solutions at the host level but I will not cover
them in this article.
{{< rawhtml >}}
<script src="https://unpkg.com/leaflet@latest/dist/leaflet.js"></script>
<link href="https://unpkg.com/leaflet@latest/dist/leaflet.css" rel="stylesheet"/>
<div id="osm-map"></div>
<script type="text/javascript">
var element = document.getElementById('osm-map');
element.style = 'height:500px;';
var map = L.map(element);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
var center = L.latLng('49.708', '2.516');
map.setView(center, 7);
L.marker(L.latLng('48.8566969', '2.3514616')).addTo(map); // storage france
L.marker(L.latLng('50.4549568', '3.9519580')).addTo(map); // storage belgium (x2)
</script>
<p><!-- space --></p>
{{< /rawhtml >}}
# Backup management
Storage layer relies on ZFS pools. There is a wonderful free software called
[Sanoid](https://github.com/jimsalterjrs/sanoid) to take snapshots of your datasets and manage their retention. Here is
an example of configuration on a storage host:
```
[zroot]
hourly = 0
daily = 0
monthly = 0
yearly = 0
autosnap = no
autoprune = no
[storage/xxx]
use_template = storage
[storage/yyy]
use_template = storage
[storage/zzz]
use_template = storage
[template_storage]
hourly = 0
daily = 31
monthly = 12
yearly = 10
autosnap = yes
autoprune = yes
```
Where *storage/xxx*, *storage/yyy*, and *storage/zzz* are datasets exposed to my family computers. With this
configuration, I am able to keep 10 years of snapshots. This may change over time depending on disk space, performance
or retention requirements. The *zroot* dataset has no snapshot nor prune policy but is declared in the configuration for
monitoring purpose.
Sanoid is compatible with FreeBSD but it requires [system
changes](https://github.com/jimsalterjrs/sanoid/blob/master/FREEBSD.readme). You'll need an "sh" compatible shell to be
compatible with mbuffer. I've chosen to install and use "bash" because I'm familiar with it on GNU/Linux servers.
To automatically create and prune snapshots, I've created a cron job that runs every minute:
```
* * * * * /usr/local/sbin/sanoid --cron --verbose >> /var/log/sanoid.log
```
# Remote sync
Sanoid comes with a tool to sync local snapshots with a remote host called
[Syncoid](https://github.com/jimsalterjrs/sanoid#syncoid). It is similar to "rsync" but for ZFS snapshots. If the
synchronization fails in the middle, Syncoid can **resume** the replication where it was left, without restarting from
zero. It also supports **compression** on the wire. This is handy for low bandwidth networks like the one I have. To be
able to send dataset to remote destination, I've set up direct SSH communication (via the VPN) with ed25519 keys.
Then cron jobs for automation:
```
0 2,6 * * * /usr/local/sbin/syncoid storage/xxxxx root@storage2:storage/xxxxx --no-sync-snap >> /var/log/syncoid/xxxxx.log 2>&1
0 3,7 * * * /usr/local/sbin/syncoid storage/xxxxx root@storage3:storage/xxxxx --no-sync-snap >> /var/log/syncoid/xxxxx.log 2>&1
```
Beware, I use the "root" user for this connection. This can be a **security flow**. You should create a user with low
privileges and possibly use "sudo" with a restriction to the command. You should disable root login over SSH. The
countermeasure I've implemented is to disable password authentication on the root user ("*PermitRootLogin
without-password*" in sshd_config file from OpenSSH server). I've also restricted SSH connections to the VPN and local
networks only. No public network allowed.
# Local usage
Now, ZFS snapshots are automatically created and replicated. How can we start using the service? *I want to send my
data!* Every location has its own storage server. The idea is to use the local network and send data to the local server
and let the Sanoid/Syncoid couple handle the rest over the VPN for data safety.
At the beginning, all my family members were using [Microsoft Windows](https://en.wikipedia.org/wiki/Microsoft_Windows)
(10). To provide the most user friendly experience, I thought it was a good idea to create a
[CIFS](https://en.wikipedia.org/wiki/Server_Message_Block) share with
[Samba](https://en.wikipedia.org/wiki/Samba_(software)). The authentication system was a pain to configure but the
network drive was recognized and it worked... for a while. Every single Samba update on the storage server broke the
share. I've lost countless hours debugging this s\*\*t.
I started to show them alternatives to Windows. One day, my wife accepted to change. She opted for
[Kubuntu](https://kubuntu.org/). Then my parents-in-law changed too. I was able to remove the Samba share and use
[NFS](https://en.wikipedia.org/wiki/Network_File_System) instead. This changed my life. The network folder has never
stopped working since the switch. For my personal use, I use [rsync](https://en.wikipedia.org/wiki/Rsync) and cron to
**automatically** send my local folders.
The storage infrastructure looks like this (storage1 example):
{{< rawhtml >}}
<p style="text-align: center;"><img src="/geographic-distribution-diagram.svg" alt="Geographic distribution diagram" style="width: 50%;"></p>
{{< /rawhtml >}}
Syncoid is configured to replicate to other nodes:
{{< rawhtml >}}
<p style="text-align: center;"><img src="/geographic-distribution-diagram-2.svg" alt="Geographic distribution part 2" style="width: 50%;"></p>
{{< /rawhtml >}}
The most important rule is to **strictly forbid writes** on the **same dataset** on two **different locations** at the
**same time**. This
setup is not "[multi-master](https://en.wikipedia.org/wiki/Multi-master_replication)" compliant at all.
In the end, the data management is fully automated. Data losses belong to the past.

View file

@ -0,0 +1,76 @@
---
title: "Hardware adventures and operating systems installation"
date: 2020-07-24T18:00:00+02:00
---
At the beginning of the project, the goal was to create a single storage server at my apartment. So I bought a [fancy
case](https://www.ldlc.com/fr-be/fiche/PB00181814.html) with racks in front to hot replace disks and I retrieved an
[Intel NUC motherboard](https://www.intel.com/content/www/us/en/products/boards-kits/nuc/boards.html) from work. It had
only two SATA ports available to connect disks which is not enough to plug at least four disks: one for the system and
three for the storage. I bought a [PCI RAID card](https://www.amazon.fr/gp/product/B0001Y7PU8) to add four slots. I
connected two small SSD for the system and four data disks, then installed FreeBSD without any issue. I started to copy
data to the storage space when a noisy alarm[^1] began to wake everybody up in the building. This was unbearable. I
decided to buy a *micro ATX* motherboard with processor and memory to replace the Intel NUC board. Wrong. I confused
[micro ATX](https://en.wikipedia.org/wiki/MicroATX) with [mini ITX](https://en.wikipedia.org/wiki/Mini-ITX) formats. The
first one was too big to fit in the box. So I bought a classic ATX case with a cheap power supply and 3x2TB disks from
work. **Storage1** was born.
At that point, I had a working storage server and some pieces to build a second one. At the same time, my wife and I had
a baby. My office at home became the newborn bedroom. I paused this project for a year to focus on my family. Then, we
bought a house with plenty of space to handle life serenely.
During the move, I unpacked my very first computer that I had assembled in 2008. The only missing thing was a physical
slot to rack the fourth disk. I bought a [low cost ATX case](https://www.amazon.fr/gp/product/B00LA7PC6Y/) and moved
every piece into. I started before work on a Friday but didn't finish on time. My home office was covered with computers
pieces all day long. When I finished work, I went back to the project when a friendly neighbor called on me for help
because his computer crashed. Right before going to bed, I tried to connect the power button to the motherboard without
instructions, and it didn't work. I finally found it on the web and made it work, at midnight. **Storage 2** was born.
It runs on a quite old hardware (10+ years). I thought it would be easy to install FreeBSD because it was created in the
90s[^2]. I tried to boot from USB but the stick was not recognized. I burnt a CD-ROM with version 12, the latest release
at that time. The installer was not able to load because of a [LUA
error](https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=234031) in the bootloader. In the comments and on forums, some
people managed to make version 11 work. I burnt a CD-ROM with version 11, same result. After having lost an afternoon of
my time and two CD-ROMs, I went back into my comfort zone and installed a Debian 10 with success.
Recently, my family offered me the missing hardware pieces to finalize the third storage host. The big one with 4TB
disks in the mini case. The one I had bought at the beginning of the project. In the end, it is not so practical. Disks
are not fixed to the rack. They can move back and forth a few centimeters. Some disks were not recognized by the system
because they were not connected. I pushed all of them with a screwdriver to ensure they were plugged into the SATA
connector. For the price, I expected it to work out-of-the-box. I was surprised to find four SATA ports on the
motherboard where I expected five or six. I removed one system disk. Goodbye dirty hack with adhesive tape to stick the
second SSD! Let's join your friends in the stock. **Storage 3** was born.
Here is the detailed list of components:
| Host | Component | Reference |
| -------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| storage1 | Case | [Antec One](https://media.ldlc.com/ld/products/00/01/00/62/LD0001006251_2.jpg) |
| | Power supply | [Antec Basiq Series VP350P](https://media.ldlc.com/ld/products/00/00/89/95/LD0000899597_2.jpg) |
| | Motherboard | [Gigabyte GA-B150M-DS3H](https://media.ldlc.com/ld/products/00/03/45/35/LD0003453579_2.jpg) |
| | CPU | [Intel Celeron G3900 (2.8 GHz)](https://media.ldlc.com/ld/products/00/01/47/39/LD0001473956_2_0001473966_0001571304_0001571323_0003614881.jpg) |
| | RAM | [G.Skill Aegis 4 Go (1 x 4 Go) DDR4 2133 MHz CL15](https://www.ldlc.com/fr-be/fiche/PB00202287.html) |
| | System disks | [LDLC SSD F2 32 GB](https://media.ldlc.com/ld/products/00/03/42/11/LD0003421194_2_0003421246.jpg) (x2) |
| | Data disks | 2TB HDD 3.5" (x3) |
| storage2 | Case | [Advance Grafit](https://www.amazon.fr/gp/product/B00LA7PC6Y/) |
| | Power supply | No reference found |
| | Motherboard | Asus M2A-VM HDMI |
| | CPU | AMD Athlon 64 X2 5000+ Socket AM2 |
| | RAM | G.Skill Kit Extreme2 2 x 1 Go PC6400 PK (x2) |
| | System disk | Recycled 160GB HDD 3.5" |
| | Data disks | 1TB HDD 3.5" (x3) |
| storage3 | Case | [In Win IW-MS04](https://www.ldlc.com/fr-be/fiche/PB00181814.html) |
| | Motherboard | [ASRock H310CM-ITX/AC](https://www.ldlc.com/fr-be/fiche/PB00275155.html) |
| | CPU | [Intel Celeron G4920 (3.2 GHz)](https://www.ldlc.com/fr-be/fiche/PB00247186.html) |
| | RAM | [G.Skill Aegis 4 Go (1 x 4 Go) DDR4 2133 MHz CL15](https://www.ldlc.com/fr-be/fiche/PB00202287.html) |
| | System disk | [LDLC SSD F2 32 GB](https://media.ldlc.com/ld/products/00/03/42/11/LD0003421194_2_0003421246.jpg) |
| | Data disks | 4TB HDD 3.5" (x3) |
Despite heterogeneous components, storage servers have been successfully running for a while now.
[^1]: Later, I found out that the noise was coming from the disk backplane and not the motherboard. There is a buzzer
that emits a sound sequence depending on the detected anomaly. At the apartment and at my current house in the summer,
the temperature in the room was too high (more than 29°C). I moved the host in a cold place. Problem solved.
[^2]: FreeBSD [initial release](https://en.wikipedia.org/wiki/FreeBSD) was on November 1, 1993.

View file

@ -0,0 +1,141 @@
---
title: "Increased observability with the TIG stack"
date: 2020-08-10T18:00:00+02:00
---
[Observability](https://en.wikipedia.org/wiki/Observability) has become a buzzword lately. I must admit, this is one of
the many reasons why I use it in the title. In reality, this article will talk about fetching measurements and creating
beautiful graphs to feel like [detective Derrick](https://en.wikipedia.org/wiki/Derrick_(TV_series)), an *old* and wise
detective solving cases by encouraging criminals to confess by themselves.
With the recent [Go](https://golang.org/) programming language [gain of
popularity](https://opensource.com/article/17/11/why-go-grows), we have seen a lot of new software coming into the
database world: [CockroachDB](https://www.cockroachlabs.com/), [TiDB](https://pingcap.com/products/tidb),
[Vitess](https://vitess.io/), etc. Among them, the **TIG stack**
([**T**elegraf](https://github.com/influxdata/telegraf), [**I**nfluxDB](https://github.com/influxdata/influxdb) and
[**G**rafana](https://github.com/grafana/grafana)) has become a reference to gather and display metrics.
The goal is to see the evolution of different resources usage (memory, processor, storage space), power consumption,
environment variables (temperature, humidity), on every single host of the infrastructure.
# Telegraf
The first component of the stack is Telegraf, an agent that can fetch metrics from multiple sources
([input](https://github.com/influxdata/telegraf/tree/master/plugins/inputs)) and write them to multiple destinations
([output](https://github.com/influxdata/telegraf/tree/master/plugins/outputs)). There are tens of built-in plugins
available! You can even gather a custom source of data with
[exec](https://github.com/influxdata/telegraf/tree/master/plugins/inputs/exec) with an expected
[format](https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md).
I configured Telegraf to fetch and send metrics every minute (*interval* and *flush_interval* in the *agent* section is
*"60s"*) which is enough for my personal usage. Most of the plugins I use are built-in: cpu, disk, diskio, kernel, mem,
processes, system, zfs, net, smart, ping, etc.
The [zfs](https://github.com/influxdata/telegraf/tree/master/plugins/inputs/zfs) plugin fetches ZFS pool statistics like
size, allocation, free space, etc, on FreeBSD but not [on Linux](https://github.com/influxdata/telegraf/issues/2616).
The issue is known but has not been merged upstream yet. So I have developed a simple Python snippet to fill the gap on
my only storage server running on Linux:
```
#!/usr/bin/python
import subprocess
def parse_int(s):
return str(int(s)) + 'i'
def parse_float_with_x(s):
return float(s.replace('x', ''))
def parse_pct_int(s):
return parse_int(s.replace('%', ''))
if __name__ == '__main__':
measurement = 'zfs_pool'
pools = subprocess.check_output(['/usr/sbin/zpool', 'list', '-Hp']).splitlines()
output = []
for pool in pools:
col = pool.split("\t")
tags = {'pool': col[0], 'health': col[9]}
fields = {}
if tags['health'] == 'UNAVAIL':
fields['size'] = 0
else:
fields['size'] = parse_int(col[1])
fields['allocated'] = parse_int(col[2])
fields['free'] = parse_int(col[3])
fields['fragmentation'] = '0i' if col[6] == '-' else parse_pct_int(col[6])
fields['capacity'] = parse_int(col[7])
fields['dedupratio'] = parse_float_with_x(col[8])
tags = ','.join(['{}={}'.format(k, v) for k, v in tags.items()])
fields = ','.join(['{}={}'.format(k, v) for k, v in fields.items()])
print('{},{} {}'.format(measurement, tags, fields))
```
Called by the following input:
```
[[inputs.exec]]
commands = ['/opt/telegraf-plugins/zfs.py']
data_format = "influx"
```
This exec plugin does exactly the same job as the zfs input running on FreeBSD.
All those metrics are sent to a single output, InfluxDB, hosted on the monitoring server.
# InfluxDB
Measurements can be stored in a time series database which is designed to organize data around time. InfluxDB is a
perfect use case for what we need. Of course, there are other time series databases. I've chosen this one because it is
well documented, it fits my needs and I wanted to learn new things.
[Installation](https://docs.influxdata.com/influxdb/v1.8/introduction/install/) is straightforward. I've enabled
[HTTPS](https://docs.influxdata.com/influxdb/v1.8/administration/https_setup/) and
[authentication](https://docs.influxdata.com/influxdb/v1.8/administration/authentication_and_authorization/#set-up-authentication).
I use a simple setup with only one node in the *cluster*. No sharding. Only one database. Even if there is not so many
metrics sent by Telegraf, I've created a default [retention
policy](https://docs.influxdata.com/influxdb/v1.8/query_language/manage-database/#retention-policy-management) to store
two years of data which is more than enough. A new default retention policy will become the default route to store all
your new points. Don't be afraid to see all the existing measurements vanished. Nothing has been deleted. They just are
under the previous policy and need to be
[moved](https://community.influxdata.com/t/applying-retention-policies-to-existing-measurments/802). You should define a
[backup](https://docs.influxdata.com/influxdb/v1.8/administration/backup_and_restore/) policy too.
# Grafana
Now that we are able to gather and store metrics, we need to visualize them. This is the role of
[Grafana](https://grafana.com/). During my career, I played with
[Graylog](https://docs.graylog.org/en/3.2/pages/dashboards.html), [Kibana](https://www.elastic.co/kibana) and Grafana.
The last one is my favorite. It is generally blazing fast! Even on a Raspberry Pi. The look and feel is amazing. The
theme is dark by default but I like the light one.
I have created four dashboards:
- **system**: load, processor, memory, system disk usage, disk i/o, network quality and bandwidth
- **storage**: ZFS pool allocation, capacity, fragmentation and uptime for each disk
- **power consumption**: kWh used per day, week, month, year, current UPS load, price per year (more details on a next
post)
- **sensors**: ambient temperature, humidity and noise (more details on a next post)
Every single graph has a *$host* [variable](https://grafana.com/docs/grafana/latest/variables/templates-and-variables/)
at the dashboard level to be able to filter metrics per host. On top of the screen, a dropdown menu is automatically
created to select the host based on an InfluxDB query.
And because a picture is worth a thousand words, here are some screenshots of my own graphs:
[![System](/grafana-system.png)](/grafana-system.png)
[![Storage](/grafana-storage.png)](/grafana-storage.png)
[![Power consumption](/grafana-power-consumption.png)](/grafana-power-consumption.png)
[![Sensors](/grafana-sensors.png)](/grafana-sensors.png)
# Infrastructure
To sum this up, the infrastructure looks like this:
![TIG stack](/monitoring-tig.svg)
Whenever I want, I can sit back on a comfortable sofa, open a web browser and let the infrastructure speak for itself.
Easy, right?

View file

@ -0,0 +1,67 @@
---
title: "Infrastructure overview"
date: 2020-07-20T18:30:00+02:00
---
The idea behind this infrastructure is to run on commodity servers. No need to buy big racks of expensive servers as we
see in data centers. Simple homemade computers will do the job. At work, I have access to cheap hard drives that were
used in servers and either are out of warranty or not suitable for enterprise workload. They generally are half their
market price. I have a mix of brand new and re-used drives to reduce the risk of having two disks failing at the same
time in the same host.
There are three components in the infrastructure:
* **storage** servers that hold the data
* **monitoring** server that grabs metrics and sends alerts
* **vps**[^1] server used to create a VPN[^2] and watch for monitoring server availability
{{< rawhtml >}}
<p style="text-align: center;"><img src="/infrastructure-overview.svg" alt="Infrastructure overview" style="width: 65%;"></p>
{{< /rawhtml >}}
# Storage
Every storage server is designed to be hosted on a different location. Each one could be unplugged from a location then
plugged somewhere else and work the same way as before. They require an Internet access to be able to contact the VPS to
join the VPN.
The technology that holds data is **[ZFS](https://en.wikipedia.org/wiki/ZFS)**. I have the chance to use it at work for
production workloads and it makes life way easier. I am used to manage GNU/Linux servers
([Debian](https://www.debian.org/)) and I know that [FreeBSD](https://www.freebsd.org/) has built-in ZFS support, so I
wanted to give it a try. I didn't choose [FreeNAS](https://www.freenas.org/) because I wanted to do everything by myself
to learn and use only the features I needed.
The right balance I found to maximize available disk space while keeping data safe is to use **three disks** in a
[RAID-Z](https://en.wikipedia.org/wiki/ZFS#RAID_(%22RaidZ%22)). Storage servers are allowed to lose one disk at a time
without breaking the service. In the meantime, almost all the cumulative space is available to use. Datasets are
configured to use **lz4** compression because it saves disk space without pushing too much pressure on the CPU.
| Host | Disk capacity |
| -------- | ------------: |
| storage1 | 5.44T |
| storage2 | 2.72T |
| storage3 | 10.9T |
# Monitoring
Like any system administrator, I want to be alerted when something goes wrong on the infrastructure. I also want to
browse the history with graphs to see trends. There was a [Raspberry Pi](https://www.raspberrypi.org/) waiting to be
used in a drawer. It is now connected to the Wi-Fi network somewhere in the house, perfectly hidden, to do this job in
the background.
# VPS
I am not a network engineer. Actually, this is not my job and I don't want it to be. There are numerous experts in the
field that do this very well and I am thankful to them. But a computer without network connectivity is not very useful.
When self-hosting, you have to deal with your ISP modem settings. There is no standard as far as I know. Mine has no
fixed public IPv4 address. I tried to develop scripts to automatically update a subdomain name with the current public
IP address and try to contact it from the outside. The name worked, but the communication always failed.
To solve this problem, I [rent a VPS](https://www.ovhcloud.com/fr/vps/) hosted close to storage locations and I have
configured an [OpenVPN](https://openvpn.net/) server. This is a single point of failure and a *bottleneck* because all
the traffic goes to this server to communicate with others. In fact, Internet bandwidth at home is the real bottleneck
so the VPS should not be a problem. It also acts as the entry point from the outside world for metrics and monitoring
websites.
[^1]: [Virtual Private Server](https://en.wikipedia.org/wiki/Virtual_private_server)
[^2]: [Virtual Private Network](https://en.wikipedia.org/wiki/Virtual_private_network)

View file

@ -0,0 +1,112 @@
---
title: "Network configuration with OpenVPN"
date: 2020-07-27T18:00:00+02:00
---
Networking is hard. Dealing with ISP modem settings is even harder. Mine doesn't have a static public IP address by
default. If the modem reboots, it is likely that it will be assigned a new one. For regular people, it is not a problem
for browsing the Internet. But for hackers like us, that means we cannot use the IP address itself to reach the private
network from the outside world. It becomes a problem when we try to join hosts in different networks.
For your information, this is the price my ISP would like me to pay for this "option":
![Fixed IP option](/fixed-ip-option.png)
This is insane!
The first idea was to deploy a script on each host that discover the public IP address and register an A record on a
given subdomain name. This job could have been run by a cron daemon. It would transform a dynamic IP address into a
predictable name. It was like the [no-ip](https://www.noip.com/) service. It worked. I was able to know the home public
IP address.
Then, I started to use [port
mapping](https://www.proximus.be/support/en/id_sfaqr_ports_mapping/personal/support/internet/internet-at-home/advanced-settings/internet-port-mapping-on-your-modem.html#/bbox3)
to redirect a given port on my router to a host in the private network. By default, some protocols like SSH, HTTP and
HTTPS are [not
open](https://www.proximus.be/support/en/id_sfaqr_ports_unblock_secu/personal/support/internet/security-and-protection/internet-ports-and-security/open-internet-ports.html),
even if you configure port mapping correctly. You have to go on the ISP website and lower your *security level* from
high to low. At my apartment, I successfully managed to reach some port from the outside, but never on my current house.
The major problem of this procedure is its **complexity** and the fact it **highly depends on your ISP
devices/settings**. I had to find a simpler solution.
Here comes [OpenVPN](https://openvpn.net/). It's an open-source software which creates private networks on public
networks. It uses encryption to secure connection between each host to keep your transport safe. The initial setup is
quite long and complex but you just have to follow this [great
tutorial](https://www.digitalocean.com/community/tutorials/how-to-set-up-an-openvpn-server-on-debian-10) and it will
work like a charm. The drawback is you'll need a single point to act as a server. I choose to [rent a
VPS](https://www.ovhcloud.com/fr/vps/) for a few euros per month. It has a fixed IP address and a decent bandwidth for
our usage. It runs on Debian but there are plenty of operating systems available.
The OpenVPN certificate management can be a bit disturbing at first. I use my monitoring host as CA[^1] to keep trust at
home and every host has its own client certificate. I've set static IP addressing up to always assign the same address
to clients. I've enabled direct communication between clients because storage servers will send snapshots to each
others. I didn't configure clients to forward all their packets to the VPN server because the goal here is not to hide
behind it for privacy.
I have changed the following settings on the VPN server:
```
topology subnet ; declare a subnet like home
server 10.xx.xx.xx 255.xx.xx.xx ; with the range you like
client-to-client ; allow clients to talk to each other
client-config-dir /etc/openvpn/ccd ; static IP configuration per client
ifconfig-pool-persist /var/log/openvpn/ipp.txt ; IP lease settings
```
Example of *ipp.txt* file:
```
storage1,10.xx.xx.xx
storage2,10.yy.yy.yy
storage3,10.zz.zz.zz
```
Example of */etc/openvpn/ccd/storage1.user* file:
```
ifconfig-push 10.xx.xx.xx 255.xx.xx.xx
```
The network configuration declared in *client-config-dir* must match the one in *ipp.txt*.
The configuration generated by the *make_config.sh* script (see the tutorial mentioned above) can be written to:
* */etc/openvpn/client.conf* (Debian)
* */usr/local/etc/openvpn/openvpn.conf* (FreeBSD)
When the OpenVPN service is started, you should be able to see the tun interface up and running.
```
tun0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> metric 0 mtu 1500
options=80000<LINKSTATE>
inet6 fe80::xxxx:xxxx:xxxx:xxxx%tun0 prefixlen 64 scopeid 0x3
inet 10.xx.xx.xx --> 10.xx.xx.xx netmask 0xffffff00
groups: tun
nd6 options=21<PERFORMNUD,AUTO_LINKLOCAL>
Opened by PID 962
```
```
3: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN group default qlen 100
link/none
inet 10.xx.xx.xx/xx brd 10.xx.xx.xx scope global tun0
valid_lft forever preferred_lft forever
```
Et voilà! Every server is now part of a private network:
```
monitoring ~ # nmap -sn 10.xx.xx.xx/xx
Starting Nmap 7.70 ( https://nmap.org ) at 2020-07-13 17:28 CEST
Nmap scan report for vps (10.xx.xx.xx)
Host is up (0.018s latency).
Nmap scan report for 10.xx.xx.xx
Host is up (0.032s latency).
Nmap scan report for 10.xx.xx.xx
Host is up (0.24s latency).
Nmap scan report for 10.xx.xx.xx
Host is up (0.22s latency).
Nmap scan report for 10.xx.xx.xx
Host is up.
Nmap done: xx IP addresses (5 hosts up) scanned in 13.11 seconds
```
[^1]: [Certificate Authority](https://en.wikipedia.org/wiki/Certificate_authority)

View file

@ -0,0 +1,146 @@
---
title: "Power consumption and failures prevention"
date: 2020-08-14T18:00:00+02:00
---
Providing a full storage service means having computers up 24x7. On one hand, if we power off the local storage server
when we aren't using it, we'll have to find a solution to respect the backup policy and synchronize with remote servers
that could be down at the moment. On the other hand, if we let the storage server up all the time, it will consume
unnecessary resources and throw money down the drain. I deeply know that a personal computer, which is idle most of the
time, doesn't consume so much power. This is my conviction. But how to verify it?
With [observability]({{< ref "posts/increased-observability-with-the-tig-stack" >}}), I thought it would be easy to
gather power consumption via built-in sensors. I tried something that I know,
[lm_sensors](https://hwmon.wiki.kernel.org/lm_sensors), which is included in the Linux kernel and exposes CPU
temperatures, fans speed, power voltages, etc.
```
storage2 ~ # sensors
k8temp-pci-00c3
Adapter: PCI adapter
Core0 Temp: +30.0°C
Core0 Temp: +22.0°C
Core1 Temp: +30.0°C
Core1 Temp: +16.0°C
acpitz-acpi-0
Adapter: ACPI interface
temp1: +40.0°C (crit = +75.0°C)
atk0110-acpi-0
Adapter: ACPI interface
Vcore Voltage: +1.10 V (min = +1.45 V, max = +1.75 V)
+3.3 Voltage: +3.39 V (min = +3.00 V, max = +3.60 V)
+5.0 Voltage: +4.97 V (min = +4.50 V, max = +5.50 V)
+12.0 Voltage: +12.22 V (min = +11.20 V, max = +13.20 V)
CPU FAN Speed: 3391 RPM (min = 0 RPM, max = 1800 RPM)
CHASSIS FAN Speed: 0 RPM (min = 0 RPM, max = 1800 RPM)
POWER FAN Speed: 1662 RPM (min = 0 RPM, max = 1800 RPM)
CPU Temperature: +26.0°C (high = +90.0°C, crit = +125.0°C)
MB Temperature: +37.0°C (high = +70.0°C, crit = +125.0°C)
```
The ACPI interface returns some voltages measurements. But I doubt they can be used to find the instant consumption in
watt (W) and extrapolate the consumption over time in kilowatt-hour (kWh). On laptops, such information can be computed
from battery statistics. Unfortunately, all computers of the infrastructure are desktops without batteries.
I needed to buy a product. A [lot](https://modernsurvivalblog.com/alternative-energy/kill-a-watt-meter/)
[of](https://www.howtogeek.com/107854/the-how-to-geek-guide-to-measuring-your-energy-use/)
[websites](https://www.pcmag.com/news/how-to-measure-home-power-usage)
[talk](https://michaelbluejay.com/electricity/measure.html) about how to measure power consumption for computers and
even for the whole house. The common thing that comes out is the recommendation to use a
[wattmeter](https://en.wikipedia.org/wiki/Wattmeter).
{{< rawhtml >}}
<p style="text-align: center;"><img src="/zaeel-wattmetre.jpg" alt="Wattmeter" style="width: 25%;"></p>
{{< /rawhtml >}}
It's an instrument which can be plugged between the power outlet and your device to measure how much energy is consumed
instantaneously (W), over time (kWh) and even the total price if you have configured the kWh price. There is a LCD to
display the results. A wattmeter is cheap. I've bought [this
model](https://www.amazon.fr/dp/B07GN5NPDJ/ref=cm_sw_r_tw_dp_x_FMMiFb2911HN7) which does a good job. Sadly, we cannot
gather the data from the LCD to load them to the metrics infrastructure easily. It also lacks of precision for the
price. We can enter only two digits after the floating point while the energy provider gives us a price with five
digits.
Speaking of the price, my [energy provider](https://www.engie.be/fr/) publishes a beautiful but beyond understanding
[grid of prices](https://www.engie.be/fr/energie/electricite-gaz/prix-conditions). They are dependent on the pack of
products, the region and the distributor. They change over time. You can have an electrical counter for the day and for
the night. Moreover, price is displayed in cents and not euros! I called them to have a price estimation. Come on, we
are in a digitized world. They should, at least, display the current price of the contract somewhere in the customer
panel.
During my researches, I found that we could use an uninterruptible power supply (UPS) to gather power consumption
metrics. As a bonus, it is able to protect from power variations and interruptions that could harm computers. However,
they are quite expensive. Their prices range from 50€ to hundreds of euros. As I'm a total newbie in this domain, I've
read this detailed [guide](https://www.materiel.net/guide-achat/g13-les-onduleurs-et-prises-parafoudre/1/) (FR) to gain
some knowledge. So I decided to buy an [APC Back-UPS Pro
550](https://www.apc.com/shop/be/en/products/APC-Power-Saving-Back-UPS-Pro-550/P-BR550GI).
{{< rawhtml >}}
<p style="text-align: center;"><img src="/apc-back-ups-pro-550.jpg" alt="UPS" style="width: 25%;"></p>
{{< /rawhtml >}}
It has an USB interface to control it with [apcupsd](https://en.wikipedia.org/wiki/Apcupsd) and display power
information with the "apcaccess" binary. It's compatible with both Debian and FreeBSD and it even has a [telegraf
plugin](https://github.com/influxdata/telegraf/tree/master/plugins/inputs/apcupsd)!
```
storage1 ~ # /usr/local/sbin/apcaccess
APC : 001,036,0867
DATE : 2020-07-30 15:56:46 +0200
HOSTNAME : storage1
VERSION : 3.14.14 (31 May 2016) freebsd
UPSNAME : storage1
CABLE : USB Cable
DRIVER : USB UPS Driver
UPSMODE : Stand Alone
STARTTIME: 2020-07-26 18:28:21 +0200
MODEL : Back-UPS RS 550G
STATUS : ONLINE
LINEV : 234.0 Volts
LOADPCT : 10.0 Percent
BCHARGE : 100.0 Percent
TIMELEFT : 37.5 Minutes
MBATTCHG : 5 Percent
MINTIMEL : 3 Minutes
MAXTIME : 0 Seconds
SENSE : Medium
LOTRANS : 176.0 Volts
HITRANS : 282.0 Volts
ALARMDEL : No alarm
BATTV : 13.7 Volts
LASTXFER : No transfers since turnon
NUMXFERS : 0
TONBATT : 0 Seconds
CUMONBATT: 0 Seconds
XOFFBATT : N/A
SELFTEST : NO
STATFLAG : 0x05000008
SERIALNO : 4B1939P01928
BATTDATE : 2019-09-23
NOMINV : 230 Volts
NOMBATTV : 12.0 Volts
NOMPOWER : 330 Watts
FIRMWARE : 857.L7 .I USB FW:L7
END APC : 2020-07-30 15:57:32 +0200
```
The 550 is the first model of the Back-UPS Pro range so it has [IEC C13 power
plugs](https://en.wikipedia.org/wiki/IEC_60320#C13/C14_coupler) only, suitable for computers, but no [Euro/French
plugs](https://en.wikipedia.org/wiki/AC_power_plugs_and_sockets#CEE_7.2F5_socket_and_CEE_7.2F6_plug_.28French.3B_Type_E.29)
compatible with any electrical device. As I connect only a single computer to the UPS, this is the most economical
solution.
Once the data had been fed to the observability platform, I was able to import this [beautiful
dashboard](https://grafana.com/grafana/dashboards/10835) from the Grafana community. I've customized it to my own needs
and here is the result:
[![storage1](/power-consumption-storage1.png)](/power-consumption-storage1.png)
[![storage2](/power-consumption-storage2.png)](/power-consumption-storage2.png)
[![storage3](/power-consumption-storage3.png)](/power-consumption-storage3.png)
You can download my dashboard [here](/grafana-power-consumption.json).
Our winner is *storage3* which costs less than a kebab per year! The worst case is *storage2*, the old hardware, that
consumes the equivalent of an incandescent light bulb. See, the power consumption is not so bad after all.

View file

@ -0,0 +1,200 @@
---
title: "Problem detection and alerting"
date: 2020-08-07T18:00:00+02:00
---
Everything is distributed, automated and runs in perfect harmony with a common goal: protect your data. But bad things
happen, and rarely when you expect them. This is why you need to watch for services states and send a notification when
something goes wrong. Monitoring systems are well-known in the enterprise world. For our use case, we don't need to
deploy a complex infrastructure to check couple of hosts. For this reason, I choose to use the good old [Nagios
Core](https://www.nagios.org/projects/nagios-core/). It even provides a web interface for humans like us.
# How it works
There are two types of checks:
- **host**: check if host is alive or not
- **service**: check if service of a host is healthy or not
To check if a host is available, the simplest implementation is to use ping:
{{< rawhtml >}}
<p style="text-align: center;"><img src="/monitoring-host-check.svg" alt="Monitoring host check " style="width: 50%;"></p>
{{< /rawhtml >}}
For services, there is a tool to execute remote plugins called
[NRPE](https://support.nagios.com/kb/article/nrpe-agent-and-plugin-explained-612.html)[^1]. It works with a client on
the monitoring host and an agent on the remote host that executes commands on demand. The return code defines the check
result.
{{< rawhtml >}}
<p style="text-align: center;"><img src="/monitoring-service-check.svg" alt="Monitoring service check " style="width: 65%;"></p>
{{< /rawhtml >}}
Services states can be:
- **OK**: it works as expected
- **WARNING**: it works but we should take a look
- **CRITICAL**: it's broken
- **UNKNOWN**: something is wrong with the plugin configuration or communication
Plugins can define a warning and/or critical threshold to manage the expected state. For example, I would like to know
when disk space usage of a storage host goes over, say, 80% (warning) and 100% (critical). I have time to take action to
free some space or order new hard drives before it becomes critical. And if I do nothing, a higher alert will be sent if
the disk becomes full.
# Installation
My monitoring host runs on Raspbian 10:
```
apt update
apt install nagios4 monitoring-plugins
```
Installed.
By default, the web interface was broken. I had to disable the following block in the */etc/nagios4/apache2.conf* file:
```
# <Files "cmd.cgi">
# ...
# </Files>
```
For security reasons, I enabled a basic authentication (a.k.a *htaccess*) in the *DirectoryMatch* block of the same file
and created an *admin* user:
```
AuthUserFile "/etc/nagios4/htdigest.users"
AuthType Basic
AuthName "Restricted Files"
AuthBasicProvider file
Require user admin
```
In the CGI configuration file */etc/nagios4/cgi.cfg*, the default user can be set to *admin* as it is now protected by
basic security:
```
default_user_name=admin
```
Now the web interface should be up and running at http://monitoring-ip/nagios4. For my own usage, I've set up a reverse
proxy (nginx) on the VPS host to expose this interface to a public endpoint so I can access it from anywhere with my
credentials.
# Configuration
A fresh installation applies sane defaults to make Nagios work out-of-the-box. It even enables localhost monitoring.
Unfortunately, I want to check this host like any other server in the infrastructure. The first thing I do is to disable
the following include in */etc/nagios4/nagios.cfg* file:
```
#cfg_file=/etc/nagios4/objects/localhost.cfg
```
I don't want to be spammed by my monitoring system. Servers may be slower and take time to respond. The Wi-Fi connection
of the monitoring system may hang for a while... until someone reboots the host physically. During this extended period
of time (multiple hours), my family and I may sleep. I don't want to wake up with hundreds of notifications saying "Hey,
the monitoring system is DOWN!". One or two notifications is enough.
The following new templates can be defined in */etc/nagios4/conf.d/templates.cfg*:
```
define host {
name home-host
use generic-host
check_command check-host-alive
contact_groups admins
notification_options d,u,r
check_interval 5
retry_interval 5 ; retry every 5 minutes
max_check_attempts 12 ; alert at 1 hour (12x5 minutes)
notification_interval 720 ; resend notifications every 12 hours
register 0 ; template
}
define service {
name home-service
use generic-service
check_interval 5
retry_interval 5 ; retry every 5 minutes
max_check_attempts 12 ; alert at 1 hour (12x5 minutes)
notification_interval 720 ; 12 hours
register 0 ; template
}
```
There are multiple components to define:
- **hosts** (*/etc/nagios4/conf.d/hosts.cfg*): every single host
- **hostgroups** (*/etc/nagios4/conf.d/hostgroups.cfg*): groups of hosts
- **services** (*/etc/nagios4/conf.d/services.cfg*): services that will be attached to hostgroups
For example, I need to know ZFS usage of all storage servers:
- **hosts**: *storage1*, *storage2*, *storage3* with their IP addresses
- **hostgroups**: *storage-servers* that will regroup *storage1*, *storage2* and *storage3*
- **services**: *zfs_capacity* that will be attached to *storage-servers*
Host definition:
```
define host {
use home-host
host_name storage1
alias storage1
address XX.XX.XX.XX
}
```
Hostgroup definition:
```
define hostgroup {
hostgroup_name storage-servers
alias Storage servers
members storage1,storage2,storage3
}
```
Service definition:
```
define service {
use home-service
hostgroup_name storage-servers
service_description zfs_capacity
check_command check_nrpe!check_zfs_capacity
}
```
On all storage servers, we also need to define a NRPE command:
```
command[check_zfs_capacity]=/usr/local/bin/sudo /usr/local/sbin/sanoid --monitor-capacity
```
ZFS usage is now monitored!
I have repeated this process for all services I wanted to check to end up with:
[![Monitoring services](/monitoring-services.png)](/monitoring-services.png)
A single host can be in multiple hostgroups. For my tests, I always added features to *storage1*. I created a hostgroup
for each new capability and added only *storage1* to it. That means *storage1* had the same services as *storage2* and
*storage3*, and the new tested ones.
# Notifications
At work, we use [Opsgenie](https://www.atlassian.com/software/opsgenie) to define on call schedules within a team. Of
course, I don't want to receive a push notification on my phone for my home servers. This is why I choose to be notified
by e-mail. In the past, I hosted some e-mail boxes at home but I didn't want to deal with spam and SPF records to prove
to the world that my service is legit. I have a couple of [domain names](https://www.gandi.net/en/domain) with
(limited) e-mail services included. For the monitoring purpose, this is more than enough to do the job.
On Nagios, you can set the e-mail address in the contacts configuration file
*/etc/nagios4/objects/contacts.cfg*.
I followed this [great tutorial](https://www.linode.com/docs/email/postfix/postfix-smtp-debian7/) to configure
[postfix](http://www.postfix.org/) to send e-mails using the SMTP server of the provider. Secure and no more spam. I
have configured this new e-mail box on my phone so I can be alerted asynchronously and smoothly when something wrong
happens.
[^1]: Nagios Remote Plugin Executor

View file

@ -0,0 +1,47 @@
---
title: "State of Internet bandwidth in Belgium"
date: 2020-07-31T18:00:00+02:00
---
I was born and raised in a little city next to Paris in **France**. In early 2000s, the unlimited "high-speed" Internet
access revolutionized communications. No need to monopolize the phone line with a 56Kbps modem anymore. Since then, the
bandwidth has always increased. We have seen the ADSL, ADSL2 and fiber technologies. We had something called "Triple
play" offers where unlimited phone calls, TV and Internet were packed together. There were three major companies on the
market: [France Telecom/Orange](https://www.orange.fr), [Bouygues](https://www.bouyguestelecom.fr/) and
[Neuf/Cegetel/SFR](https://www.sfr.fr/) (depending on the year). Then [Free](https://www.free.fr) jumped into that
alliance and broke prices with revolutionary offers. From this time, all French ISP have "low prices" between 30 and
50€/month for "high-speed" hundreds of Mbps for both down and up thanks to the fiber deployment.
Then I moved to **Belgium** for personal reasons. My parents-in-law have chosen
[Belgacom/Proximus](https://www.proximus.be/en/personal/?) and they were happy with it so I followed their choice. This
ISP has deployed the VDSL technology which can be "fast". My first apartment was very close to the DSLAM[^1] so my
bandwidth was good enough, 50Mbps/15Mbps. The price was sensitively higher for Internet and TV only, 50€/month. If we
wanted to have a phone line, we would have added 20€ to the monthly bill and pay each phone call! You can get unlimited
phone calls for [1.19€/month](https://www.ovhtelecom.fr/telephonie/voip/decouverte.xml) only using VoIP which is the
same technology our ISP use. There is a limit to the monthly Internet volume we can consume. It was something like
600GB/month when I subscribed, to 3TB now.
When I moved to my current house, I knew the bandwidth will drop. Proximus had failed to organize my move on time. You
can do it yourself on the website but if you go to a shop to reschedule the appointment, they can't do anything because
it has been scheduled online. I canceled the first rendez-vous online and they created a new one with an additional
two-week delay, one month after the move. I subscribed to [Voo](https://www.voo.be/en), the *fastest Internet of
Belgium* like they say in their [commercials](https://www.youtube.com/watch?v=LKv6LtaXIf4). Same price, better speed,
120Mbps/10Mbps... for a week. Then I had three months of packet loss, 20% on average. It was unusable. The following two
months were stable with a bandwidth drop, 70Mbps/10Mbps. Then packet loss again, 80% on average this time! Horrible. I
re-subscribed to Proximus again, with 20Mbps/6Mbps bandwidth, but it is stable since the change. All of that for
60€/month.
I called Proximus to be notified when the fiber will come to my street to finally catch up with our neighbors' speeds,
kind of. They have no plan to install it. No date. Nothing. In the meantime, my father and my grand-parents have the
**gigabit** fiber installed at home for a lower price than mine. And even if Proximus deploy it, [upload bandwidth is
limited to 100Mbps](https://www.proximus.be/en/id_cr_fiber/personal/orphans/fiber-to-your-home.html) where it can be
[200Mbps](https://www.sfr.fr/offre-internet/fibre-optique) or even [600](https://www.free.fr/freebox/freebox-delta)
[Mbps](https://boutique.orange.fr/internet/offres-fibre/livebox-up) in France. As of today, the maximum bandwidth I
could get at home is the 400Mbps/20Mbps promised by Voo, with the stability we know.
Belgian ISP, Proximus and Voo, when will you stop to steal from our pockets and start to generalize very high-speed
Internet bandwidth to the small country of ours? We are in 2020s, not in 2000s.
[^1]: [Digital subscriber line access
multiplexer](https://en.wikipedia.org/wiki/Digital_subscriber_line_access_multiplexer), the closer you are, the faster
your bandwidth is.

View file

@ -0,0 +1,24 @@
---
title: "Storage servers at home"
date: 2020-07-17T19:00:00+02:00
---
I was born in the 90s. I grew up with computers. Other generations call us "digital natives". I am lucky and proud to
work with computers every day with a database specialization. People tend to generate lots of data. It might be
administrative papers (bills, contracts, paychecks), sentimental photo albums or whatever the data is as long as it is
**their** data. At work, we pay attention to back up every data though it was the most important thing in the world. At
home, it should be the same but, in fact, nobody really cares about it unless the data is definitely gone.
My family members used to buy a single USB hard drive and regularly copy their data to it and think it is safe. This
highly depends on the frequency of the backup. Actually, they didn't copy very often. If the drive fails, they call me
to the rescue but I'm not a magician.
Another solution involves sending their data to "the cloud" because they have seen on TV this will solve all of their
problems. Cloud providers can, intentionally or unintentionally, leak their data. If we materialize data, I'm not sure
my family wants to send their physical storage cupboard to the United States for the sake of data safety. We live in
Belgium and France. There is no point of sending our data to the other side of the planet in someone else's hands.
So I decided to **self-host a set of storage servers at home** and offer this service to my own family. It has to be
simple as my parents will be the main users. I am a full-time employee and a proud dad. I have a little bit of time to
do the service maintenance. It is an opportunity for me to learn and share it to the world. Welcome to my self-hosting
project. I hope you will learn something too.

24
layouts/index.html Normal file
View file

@ -0,0 +1,24 @@
{{ define "main" }}
<h1>About</h1>
<p>
I'm <a href="https://julien.riou.xyz">Julien Riou</a>, an open source databases specialist, working for a major cloud
provider in Europe. This website is the presentation of my self-hosting projects.
</p>
<br/>
<h1>Posts</h1>
<div class="posts-list">
{{ with .Site.GetPage "/posts" }}
{{ range (sort .Data.Pages "Date" "desc" (where .Pages ".Params.private" "!=" true))}}
<div class="post">
<div class="date">{{ .PublishDate.Format "Jan 2, 2006" }}</div>
<a href="{{ .Permalink }}" title="{{ .Title }}">{{ .Title }}</a>
</div>
{{ end }}
{{ end }}
</div>
{{ end }}

14
layouts/posts/single.html Normal file
View file

@ -0,0 +1,14 @@
{{ define "main" }}
<h1>{{ .Title }}</h1>
{{ .PublishDate.Format "January 2, 2006" }}
{{ with .Params.linkedin }}
// <a href="{{ . }}">Linkedin</a>
{{ end }}
{{ with .Params.twitter }}
// <a href="{{ . }}">Twitter</a>
{{ end }}
<br/><br/>
{{ .Content }}
{{ template "_internal/disqus.html" . }}
{{ end }}

View file

@ -0,0 +1,2 @@
<!-- raw html -->
{{.Inner}}

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

BIN
static/fixed-ip-option.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View file

@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="351pt" height="254pt" viewBox="0 0 351 254" version="1.1">
<defs>
<g>
<symbol overflow="visible" id="glyph0-0">
<path style="stroke:none;" d="M 0.640625 2.265625 L 0.640625 -9.015625 L 7.03125 -9.015625 L 7.03125 2.265625 Z M 1.359375 1.546875 L 6.328125 1.546875 L 6.328125 -8.296875 L 1.359375 -8.296875 Z M 1.359375 1.546875 "/>
</symbol>
<symbol overflow="visible" id="glyph0-1">
<path style="stroke:none;" d="M 5.671875 -6.796875 L 5.671875 -5.703125 C 5.347656 -5.867188 5.007812 -5.992188 4.65625 -6.078125 C 4.300781 -6.160156 3.9375 -6.203125 3.5625 -6.203125 C 3 -6.203125 2.570312 -6.113281 2.28125 -5.9375 C 2 -5.769531 1.859375 -5.507812 1.859375 -5.15625 C 1.859375 -4.882812 1.957031 -4.671875 2.15625 -4.515625 C 2.363281 -4.367188 2.773438 -4.226562 3.390625 -4.09375 L 3.78125 -4 C 4.601562 -3.832031 5.1875 -3.585938 5.53125 -3.265625 C 5.875 -2.941406 6.046875 -2.5 6.046875 -1.9375 C 6.046875 -1.28125 5.785156 -0.757812 5.265625 -0.375 C 4.753906 0 4.050781 0.1875 3.15625 0.1875 C 2.78125 0.1875 2.390625 0.148438 1.984375 0.078125 C 1.578125 0.00390625 1.144531 -0.101562 0.6875 -0.25 L 0.6875 -1.4375 C 1.113281 -1.21875 1.53125 -1.050781 1.9375 -0.9375 C 2.351562 -0.832031 2.765625 -0.78125 3.171875 -0.78125 C 3.710938 -0.78125 4.128906 -0.875 4.421875 -1.0625 C 4.710938 -1.25 4.859375 -1.507812 4.859375 -1.84375 C 4.859375 -2.15625 4.753906 -2.394531 4.546875 -2.5625 C 4.335938 -2.726562 3.875 -2.890625 3.15625 -3.046875 L 2.765625 -3.140625 C 2.046875 -3.285156 1.53125 -3.515625 1.21875 -3.828125 C 0.90625 -4.140625 0.75 -4.566406 0.75 -5.109375 C 0.75 -5.765625 0.976562 -6.269531 1.4375 -6.625 C 1.90625 -6.988281 2.570312 -7.171875 3.4375 -7.171875 C 3.851562 -7.171875 4.25 -7.140625 4.625 -7.078125 C 5 -7.015625 5.347656 -6.921875 5.671875 -6.796875 Z M 5.671875 -6.796875 "/>
</symbol>
<symbol overflow="visible" id="glyph0-2">
<path style="stroke:none;" d="M 2.34375 -8.984375 L 2.34375 -7 L 4.71875 -7 L 4.71875 -6.109375 L 2.34375 -6.109375 L 2.34375 -2.3125 C 2.34375 -1.738281 2.421875 -1.367188 2.578125 -1.203125 C 2.734375 -1.046875 3.050781 -0.96875 3.53125 -0.96875 L 4.71875 -0.96875 L 4.71875 0 L 3.53125 0 C 2.644531 0 2.03125 -0.164062 1.6875 -0.5 C 1.351562 -0.832031 1.1875 -1.4375 1.1875 -2.3125 L 1.1875 -6.109375 L 0.34375 -6.109375 L 0.34375 -7 L 1.1875 -7 L 1.1875 -8.984375 Z M 2.34375 -8.984375 "/>
</symbol>
<symbol overflow="visible" id="glyph0-3">
<path style="stroke:none;" d="M 3.921875 -6.1875 C 3.304688 -6.1875 2.816406 -5.945312 2.453125 -5.46875 C 2.097656 -4.988281 1.921875 -4.332031 1.921875 -3.5 C 1.921875 -2.65625 2.097656 -1.992188 2.453125 -1.515625 C 2.804688 -1.035156 3.296875 -0.796875 3.921875 -0.796875 C 4.535156 -0.796875 5.019531 -1.035156 5.375 -1.515625 C 5.726562 -2.003906 5.90625 -2.664062 5.90625 -3.5 C 5.90625 -4.320312 5.726562 -4.972656 5.375 -5.453125 C 5.019531 -5.941406 4.535156 -6.1875 3.921875 -6.1875 Z M 3.921875 -7.171875 C 4.921875 -7.171875 5.703125 -6.84375 6.265625 -6.1875 C 6.835938 -5.539062 7.125 -4.644531 7.125 -3.5 C 7.125 -2.351562 6.835938 -1.453125 6.265625 -0.796875 C 5.703125 -0.140625 4.921875 0.1875 3.921875 0.1875 C 2.910156 0.1875 2.117188 -0.140625 1.546875 -0.796875 C 0.984375 -1.453125 0.703125 -2.351562 0.703125 -3.5 C 0.703125 -4.644531 0.984375 -5.539062 1.546875 -6.1875 C 2.117188 -6.84375 2.910156 -7.171875 3.921875 -7.171875 Z M 3.921875 -7.171875 "/>
</symbol>
<symbol overflow="visible" id="glyph0-4">
<path style="stroke:none;" d="M 5.265625 -5.921875 C 5.128906 -5.992188 4.984375 -6.046875 4.828125 -6.078125 C 4.679688 -6.117188 4.519531 -6.140625 4.34375 -6.140625 C 3.6875 -6.140625 3.179688 -5.925781 2.828125 -5.5 C 2.484375 -5.082031 2.3125 -4.476562 2.3125 -3.6875 L 2.3125 0 L 1.15625 0 L 1.15625 -7 L 2.3125 -7 L 2.3125 -5.90625 C 2.5625 -6.332031 2.878906 -6.648438 3.265625 -6.859375 C 3.648438 -7.066406 4.117188 -7.171875 4.671875 -7.171875 C 4.753906 -7.171875 4.84375 -7.164062 4.9375 -7.15625 C 5.03125 -7.144531 5.132812 -7.128906 5.25 -7.109375 Z M 5.265625 -5.921875 "/>
</symbol>
<symbol overflow="visible" id="glyph0-5">
<path style="stroke:none;" d="M 4.390625 -3.515625 C 3.460938 -3.515625 2.816406 -3.40625 2.453125 -3.1875 C 2.097656 -2.976562 1.921875 -2.617188 1.921875 -2.109375 C 1.921875 -1.703125 2.050781 -1.378906 2.3125 -1.140625 C 2.582031 -0.898438 2.953125 -0.78125 3.421875 -0.78125 C 4.054688 -0.78125 4.566406 -1.003906 4.953125 -1.453125 C 5.335938 -1.910156 5.53125 -2.515625 5.53125 -3.265625 L 5.53125 -3.515625 Z M 6.671875 -4 L 6.671875 0 L 5.53125 0 L 5.53125 -1.0625 C 5.269531 -0.632812 4.941406 -0.316406 4.546875 -0.109375 C 4.160156 0.0859375 3.679688 0.1875 3.109375 0.1875 C 2.390625 0.1875 1.816406 -0.015625 1.390625 -0.421875 C 0.972656 -0.828125 0.765625 -1.363281 0.765625 -2.03125 C 0.765625 -2.820312 1.023438 -3.414062 1.546875 -3.8125 C 2.078125 -4.21875 2.867188 -4.421875 3.921875 -4.421875 L 5.53125 -4.421875 L 5.53125 -4.53125 C 5.53125 -5.0625 5.351562 -5.46875 5 -5.75 C 4.65625 -6.039062 4.171875 -6.1875 3.546875 -6.1875 C 3.140625 -6.1875 2.75 -6.140625 2.375 -6.046875 C 2 -5.953125 1.632812 -5.8125 1.28125 -5.625 L 1.28125 -6.671875 C 1.695312 -6.835938 2.101562 -6.960938 2.5 -7.046875 C 2.894531 -7.128906 3.28125 -7.171875 3.65625 -7.171875 C 4.675781 -7.171875 5.429688 -6.90625 5.921875 -6.375 C 6.421875 -5.851562 6.671875 -5.0625 6.671875 -4 Z M 6.671875 -4 "/>
</symbol>
<symbol overflow="visible" id="glyph0-6">
<path style="stroke:none;" d="M 5.8125 -3.578125 C 5.8125 -4.410156 5.640625 -5.054688 5.296875 -5.515625 C 4.953125 -5.972656 4.46875 -6.203125 3.84375 -6.203125 C 3.226562 -6.203125 2.75 -5.972656 2.40625 -5.515625 C 2.0625 -5.054688 1.890625 -4.410156 1.890625 -3.578125 C 1.890625 -2.753906 2.0625 -2.113281 2.40625 -1.65625 C 2.75 -1.195312 3.226562 -0.96875 3.84375 -0.96875 C 4.46875 -0.96875 4.953125 -1.195312 5.296875 -1.65625 C 5.640625 -2.113281 5.8125 -2.753906 5.8125 -3.578125 Z M 6.953125 -0.875 C 6.953125 0.320312 6.6875 1.207031 6.15625 1.78125 C 5.632812 2.363281 4.828125 2.65625 3.734375 2.65625 C 3.328125 2.65625 2.945312 2.625 2.59375 2.5625 C 2.238281 2.507812 1.890625 2.421875 1.546875 2.296875 L 1.546875 1.171875 C 1.890625 1.359375 2.222656 1.492188 2.546875 1.578125 C 2.878906 1.671875 3.21875 1.71875 3.5625 1.71875 C 4.3125 1.71875 4.875 1.519531 5.25 1.125 C 5.625 0.726562 5.8125 0.132812 5.8125 -0.65625 L 5.8125 -1.234375 C 5.570312 -0.816406 5.265625 -0.503906 4.890625 -0.296875 C 4.523438 -0.0976562 4.082031 0 3.5625 0 C 2.707031 0 2.015625 -0.328125 1.484375 -0.984375 C 0.960938 -1.640625 0.703125 -2.503906 0.703125 -3.578125 C 0.703125 -4.660156 0.960938 -5.53125 1.484375 -6.1875 C 2.015625 -6.84375 2.707031 -7.171875 3.5625 -7.171875 C 4.082031 -7.171875 4.523438 -7.066406 4.890625 -6.859375 C 5.265625 -6.648438 5.570312 -6.34375 5.8125 -5.9375 L 5.8125 -7 L 6.953125 -7 Z M 6.953125 -0.875 "/>
</symbol>
<symbol overflow="visible" id="glyph0-7">
<path style="stroke:none;" d="M 7.1875 -3.78125 L 7.1875 -3.21875 L 1.90625 -3.21875 C 1.957031 -2.425781 2.195312 -1.820312 2.625 -1.40625 C 3.050781 -1 3.644531 -0.796875 4.40625 -0.796875 C 4.84375 -0.796875 5.269531 -0.847656 5.6875 -0.953125 C 6.101562 -1.066406 6.515625 -1.226562 6.921875 -1.4375 L 6.921875 -0.359375 C 6.515625 -0.179688 6.09375 -0.046875 5.65625 0.046875 C 5.21875 0.140625 4.78125 0.1875 4.34375 0.1875 C 3.21875 0.1875 2.328125 -0.132812 1.671875 -0.78125 C 1.023438 -1.4375 0.703125 -2.320312 0.703125 -3.4375 C 0.703125 -4.582031 1.007812 -5.488281 1.625 -6.15625 C 2.25 -6.832031 3.085938 -7.171875 4.140625 -7.171875 C 5.078125 -7.171875 5.816406 -6.863281 6.359375 -6.25 C 6.910156 -5.644531 7.1875 -4.820312 7.1875 -3.78125 Z M 6.046875 -4.125 C 6.035156 -4.75 5.859375 -5.25 5.515625 -5.625 C 5.171875 -6 4.71875 -6.1875 4.15625 -6.1875 C 3.507812 -6.1875 2.992188 -6.003906 2.609375 -5.640625 C 2.222656 -5.285156 2 -4.78125 1.9375 -4.125 Z M 6.046875 -4.125 "/>
</symbol>
<symbol overflow="visible" id="glyph0-8">
<path style="stroke:none;" d="M 1.59375 -1.0625 L 3.65625 -1.0625 L 3.65625 -8.171875 L 1.40625 -7.734375 L 1.40625 -8.875 L 3.640625 -9.328125 L 4.90625 -9.328125 L 4.90625 -1.0625 L 6.953125 -1.0625 L 6.953125 0 L 1.59375 0 Z M 1.59375 -1.0625 "/>
</symbol>
<symbol overflow="visible" id="glyph0-9">
<path style="stroke:none;" d="M 2.453125 -1.0625 L 6.859375 -1.0625 L 6.859375 0 L 0.9375 0 L 0.9375 -1.0625 C 1.414062 -1.5625 2.066406 -2.226562 2.890625 -3.0625 C 3.722656 -3.894531 4.242188 -4.429688 4.453125 -4.671875 C 4.859375 -5.128906 5.140625 -5.515625 5.296875 -5.828125 C 5.460938 -6.140625 5.546875 -6.445312 5.546875 -6.75 C 5.546875 -7.25 5.367188 -7.65625 5.015625 -7.96875 C 4.671875 -8.28125 4.21875 -8.4375 3.65625 -8.4375 C 3.257812 -8.4375 2.84375 -8.363281 2.40625 -8.21875 C 1.96875 -8.082031 1.5 -7.878906 1 -7.609375 L 1 -8.875 C 1.507812 -9.082031 1.984375 -9.238281 2.421875 -9.34375 C 2.867188 -9.445312 3.273438 -9.5 3.640625 -9.5 C 4.609375 -9.5 5.378906 -9.253906 5.953125 -8.765625 C 6.523438 -8.285156 6.8125 -7.640625 6.8125 -6.828125 C 6.8125 -6.453125 6.738281 -6.09375 6.59375 -5.75 C 6.445312 -5.40625 6.1875 -5 5.8125 -4.53125 C 5.707031 -4.40625 5.375 -4.054688 4.8125 -3.484375 C 4.257812 -2.910156 3.472656 -2.101562 2.453125 -1.0625 Z M 2.453125 -1.0625 "/>
</symbol>
<symbol overflow="visible" id="glyph0-10">
<path style="stroke:none;" d="M 5.1875 -5.03125 C 5.789062 -4.90625 6.257812 -4.632812 6.59375 -4.21875 C 6.9375 -3.8125 7.109375 -3.3125 7.109375 -2.71875 C 7.109375 -1.789062 6.789062 -1.070312 6.15625 -0.5625 C 5.53125 -0.0625 4.632812 0.1875 3.46875 0.1875 C 3.070312 0.1875 2.664062 0.144531 2.25 0.0625 C 1.84375 -0.0078125 1.414062 -0.125 0.96875 -0.28125 L 0.96875 -1.5 C 1.320312 -1.289062 1.707031 -1.132812 2.125 -1.03125 C 2.539062 -0.925781 2.976562 -0.875 3.4375 -0.875 C 4.226562 -0.875 4.828125 -1.03125 5.234375 -1.34375 C 5.648438 -1.65625 5.859375 -2.113281 5.859375 -2.71875 C 5.859375 -3.257812 5.664062 -3.6875 5.28125 -4 C 4.894531 -4.3125 4.359375 -4.46875 3.671875 -4.46875 L 2.59375 -4.46875 L 2.59375 -5.5 L 3.71875 -5.5 C 4.34375 -5.5 4.816406 -5.625 5.140625 -5.875 C 5.472656 -6.125 5.640625 -6.484375 5.640625 -6.953125 C 5.640625 -7.429688 5.46875 -7.796875 5.125 -8.046875 C 4.789062 -8.304688 4.304688 -8.4375 3.671875 -8.4375 C 3.328125 -8.4375 2.957031 -8.394531 2.5625 -8.3125 C 2.164062 -8.238281 1.726562 -8.125 1.25 -7.96875 L 1.25 -9.09375 C 1.726562 -9.226562 2.175781 -9.328125 2.59375 -9.390625 C 3.019531 -9.460938 3.414062 -9.5 3.78125 -9.5 C 4.738281 -9.5 5.492188 -9.28125 6.046875 -8.84375 C 6.609375 -8.40625 6.890625 -7.816406 6.890625 -7.078125 C 6.890625 -6.566406 6.742188 -6.128906 6.453125 -5.765625 C 6.160156 -5.410156 5.738281 -5.164062 5.1875 -5.03125 Z M 5.1875 -5.03125 "/>
</symbol>
</g>
</defs>
<g id="surface46263">
<rect x="0" y="0" width="351" height="254" style="fill:rgb(100%,100%,100%);fill-opacity:1;stroke:none;"/>
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(100%,100%,100%);fill-opacity:1;" d="M 14.351562 45.859375 L 48.351562 45.859375 L 48.351562 75.859375 L 14.351562 75.859375 Z M 14.351562 45.859375 "/>
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(100%,100%,100%);fill-opacity:1;" d="M 48.351562 75.859375 C 48.351562 78.621094 40.738281 80.859375 31.351562 80.859375 C 21.960938 80.859375 14.351562 78.621094 14.351562 75.859375 C 14.351562 73.097656 21.960938 70.859375 31.351562 70.859375 C 40.738281 70.859375 48.351562 73.097656 48.351562 75.859375 "/>
<path style="fill-rule:evenodd;fill:rgb(100%,100%,100%);fill-opacity:1;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 4.250078 3.700008 C 4.250078 3.838094 3.869414 3.950008 3.400078 3.950008 C 2.930547 3.950008 2.550078 3.838094 2.550078 3.700008 C 2.550078 3.561922 2.930547 3.450008 3.400078 3.450008 C 3.869414 3.450008 4.250078 3.561922 4.250078 3.700008 " transform="matrix(20,0,0,20,-36.65,-28.140777)"/>
<path style="fill:none;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 4.250078 3.700008 L 4.250078 5.200008 C 4.250078 5.338094 3.869414 5.450008 3.400078 5.450008 C 2.930547 5.450008 2.550078 5.338094 2.550078 5.200008 L 2.550078 3.700008 " transform="matrix(20,0,0,20,-36.65,-28.140777)"/>
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-1" x="3.167969" y="92.744249"/>
<use xlink:href="#glyph0-2" x="9.834635" y="92.744249"/>
<use xlink:href="#glyph0-3" x="14.834635" y="92.744249"/>
<use xlink:href="#glyph0-4" x="22.612413" y="92.744249"/>
<use xlink:href="#glyph0-5" x="27.890191" y="92.744249"/>
<use xlink:href="#glyph0-6" x="35.667969" y="92.744249"/>
<use xlink:href="#glyph0-7" x="43.723524" y="92.744249"/>
<use xlink:href="#glyph0-8" x="51.501302" y="92.744249"/>
</g>
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(100%,100%,100%);fill-opacity:1;" d="M 303.300781 52.460938 L 337.300781 52.460938 L 337.300781 82.460938 L 303.300781 82.460938 Z M 303.300781 52.460938 "/>
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(100%,100%,100%);fill-opacity:1;" d="M 337.300781 82.460938 C 337.300781 85.21875 329.6875 87.460938 320.300781 87.460938 C 310.910156 87.460938 303.300781 85.21875 303.300781 82.460938 C 303.300781 79.699219 310.910156 77.460938 320.300781 77.460938 C 329.6875 77.460938 337.300781 79.699219 337.300781 82.460938 "/>
<path style="fill-rule:evenodd;fill:rgb(100%,100%,100%);fill-opacity:1;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(100%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 18.697539 4.030086 C 18.697539 4.167976 18.316875 4.280086 17.847539 4.280086 C 17.378008 4.280086 16.997539 4.167976 16.997539 4.030086 C 16.997539 3.892 17.378008 3.780086 17.847539 3.780086 C 18.316875 3.780086 18.697539 3.892 18.697539 4.030086 " transform="matrix(20,0,0,20,-36.65,-28.140777)"/>
<path style="fill:none;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(100%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 18.697539 4.030086 L 18.697539 5.530086 C 18.697539 5.667976 18.316875 5.780086 17.847539 5.780086 C 17.378008 5.780086 16.997539 5.667976 16.997539 5.530086 L 16.997539 4.030086 " transform="matrix(20,0,0,20,-36.65,-28.140777)"/>
<g style="fill:rgb(100%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-1" x="292.117188" y="99.345812"/>
<use xlink:href="#glyph0-2" x="298.783854" y="99.345812"/>
<use xlink:href="#glyph0-3" x="303.783854" y="99.345812"/>
<use xlink:href="#glyph0-4" x="311.561632" y="99.345812"/>
<use xlink:href="#glyph0-5" x="316.83941" y="99.345812"/>
<use xlink:href="#glyph0-6" x="324.617188" y="99.345812"/>
<use xlink:href="#glyph0-7" x="332.672743" y="99.345812"/>
<use xlink:href="#glyph0-9" x="340.450521" y="99.345812"/>
</g>
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(100%,100%,100%);fill-opacity:1;" d="M 162.300781 201.460938 L 196.300781 201.460938 L 196.300781 231.460938 L 162.300781 231.460938 Z M 162.300781 201.460938 "/>
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(100%,100%,100%);fill-opacity:1;" d="M 196.300781 231.460938 C 196.300781 234.21875 188.6875 236.460938 179.300781 236.460938 C 169.910156 236.460938 162.300781 234.21875 162.300781 231.460938 C 162.300781 228.699219 169.910156 226.460938 179.300781 226.460938 C 188.6875 226.460938 196.300781 228.699219 196.300781 231.460938 "/>
<path style="fill-rule:evenodd;fill:rgb(100%,100%,100%);fill-opacity:1;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,39.607844%,100%);stroke-opacity:1;stroke-miterlimit:10;" d="M 11.647539 11.480086 C 11.647539 11.617976 11.266875 11.730086 10.797539 11.730086 C 10.328008 11.730086 9.947539 11.617976 9.947539 11.480086 C 9.947539 11.342 10.328008 11.230086 10.797539 11.230086 C 11.266875 11.230086 11.647539 11.342 11.647539 11.480086 " transform="matrix(20,0,0,20,-36.65,-28.140777)"/>
<path style="fill:none;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,39.607844%,100%);stroke-opacity:1;stroke-miterlimit:10;" d="M 11.647539 11.480086 L 11.647539 12.980086 C 11.647539 13.117976 11.266875 13.230086 10.797539 13.230086 C 10.328008 13.230086 9.947539 13.117976 9.947539 12.980086 L 9.947539 11.480086 " transform="matrix(20,0,0,20,-36.65,-28.140777)"/>
<g style="fill:rgb(0%,39.607844%,100%);fill-opacity:1;">
<use xlink:href="#glyph0-1" x="151.117188" y="248.345812"/>
<use xlink:href="#glyph0-2" x="157.783854" y="248.345812"/>
<use xlink:href="#glyph0-3" x="162.783854" y="248.345812"/>
<use xlink:href="#glyph0-4" x="170.561632" y="248.345812"/>
<use xlink:href="#glyph0-5" x="175.83941" y="248.345812"/>
<use xlink:href="#glyph0-6" x="183.617188" y="248.345812"/>
<use xlink:href="#glyph0-7" x="191.672743" y="248.345812"/>
<use xlink:href="#glyph0-10" x="199.450521" y="248.345812"/>
</g>
<path style="fill:none;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 16.310234 4.159187 C 12.833672 1.694539 8.180352 1.690828 4.700078 4.150008 " transform="matrix(20,0,0,20,-36.65,-28.140777)"/>
<path style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 16.611992 4.381062 L 16.064336 4.269148 L 16.316875 4.149617 L 16.37293 3.875789 Z M 16.611992 4.381062 " transform="matrix(20,0,0,20,-36.65,-28.140777)"/>
<path style="fill:none;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(100%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 12.369414 12.123836 C 14.87625 11.369734 16.874297 9.467 17.750078 7.000008 " transform="matrix(20,0,0,20,-36.65,-28.140777)"/>
<path style="fill-rule:evenodd;fill:rgb(100%,0%,0%);fill-opacity:1;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(100%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 12.008867 12.224226 L 12.437578 11.865437 L 12.373711 12.137508 L 12.553008 12.351961 Z M 12.008867 12.224226 " transform="matrix(20,0,0,20,-36.65,-28.140777)"/>
<path style="fill:none;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,39.607844%,100%);stroke-opacity:1;stroke-miterlimit:10;" d="M 12.450078 12.650008 C 15.028789 12.071883 17.183672 10.31114 18.264141 7.899226 " transform="matrix(20,0,0,20,-36.65,-28.140777)"/>
<path style="fill-rule:evenodd;fill:rgb(0%,39.607844%,100%);fill-opacity:1;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,39.607844%,100%);stroke-opacity:1;stroke-miterlimit:10;" d="M 18.410234 7.5545 L 18.466289 8.11075 L 18.277031 7.905086 L 17.998906 7.933015 Z M 18.410234 7.5545 " transform="matrix(20,0,0,20,-36.65,-28.140777)"/>
<path style="fill:none;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(100%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 16.650078 3.700008 C 13.357695 1.006258 8.694609 0.771492 5.148516 3.121297 " transform="matrix(20,0,0,20,-36.65,-28.140777)"/>
<path style="fill-rule:evenodd;fill:rgb(100%,0%,0%);fill-opacity:1;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(100%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 4.839922 3.333601 L 5.094023 2.83575 L 5.141875 3.11114 L 5.390703 3.238094 Z M 4.839922 3.333601 " transform="matrix(20,0,0,20,-36.65,-28.140777)"/>
<path style="fill:none;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 3.650078 6.250008 C 4.441289 8.905672 6.386602 11.063679 8.945977 12.125594 " transform="matrix(20,0,0,20,-36.65,-28.140777)"/>
<path style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 9.294609 12.262703 L 8.739922 12.331453 L 8.941094 12.137508 L 8.906719 11.859969 Z M 9.294609 12.262703 " transform="matrix(20,0,0,20,-36.65,-28.140777)"/>
<path style="fill:none;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,39.607844%,100%);stroke-opacity:1;stroke-miterlimit:10;" d="M 3.149492 6.762898 C 4.121953 9.552156 6.22918 11.799812 8.950078 12.950008 " transform="matrix(20,0,0,20,-36.65,-28.140777)"/>
<path style="fill-rule:evenodd;fill:rgb(0%,39.607844%,100%);fill-opacity:1;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,39.607844%,100%);stroke-opacity:1;stroke-miterlimit:10;" d="M 3.031914 6.407234 L 3.413945 6.815242 L 3.13875 6.766609 L 2.934648 6.957625 Z M 3.031914 6.407234 " transform="matrix(20,0,0,20,-36.65,-28.140777)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 20 KiB

View file

@ -0,0 +1,196 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="487pt" height="511pt" viewBox="0 0 487 511" version="1.1">
<defs>
<g>
<symbol overflow="visible" id="glyph0-0">
<path style="stroke:none;" d="M 0.640625 2.265625 L 0.640625 -9.015625 L 7.03125 -9.015625 L 7.03125 2.265625 Z M 1.359375 1.546875 L 6.328125 1.546875 L 6.328125 -8.296875 L 1.359375 -8.296875 Z M 1.359375 1.546875 "/>
</symbol>
<symbol overflow="visible" id="glyph0-1">
<path style="stroke:none;" d="M 6.25 -6.734375 L 6.25 -5.65625 C 5.914062 -5.832031 5.585938 -5.960938 5.265625 -6.046875 C 4.941406 -6.140625 4.613281 -6.1875 4.28125 -6.1875 C 3.53125 -6.1875 2.945312 -5.953125 2.53125 -5.484375 C 2.125 -5.015625 1.921875 -4.351562 1.921875 -3.5 C 1.921875 -2.644531 2.125 -1.976562 2.53125 -1.5 C 2.945312 -1.03125 3.53125 -0.796875 4.28125 -0.796875 C 4.613281 -0.796875 4.941406 -0.835938 5.265625 -0.921875 C 5.585938 -1.015625 5.914062 -1.148438 6.25 -1.328125 L 6.25 -0.265625 C 5.925781 -0.117188 5.59375 -0.0078125 5.25 0.0625 C 4.90625 0.144531 4.539062 0.1875 4.15625 0.1875 C 3.09375 0.1875 2.25 -0.144531 1.625 -0.8125 C 1.007812 -1.476562 0.703125 -2.375 0.703125 -3.5 C 0.703125 -4.632812 1.015625 -5.53125 1.640625 -6.1875 C 2.273438 -6.84375 3.132812 -7.171875 4.21875 -7.171875 C 4.570312 -7.171875 4.914062 -7.132812 5.25 -7.0625 C 5.59375 -6.988281 5.925781 -6.878906 6.25 -6.734375 Z M 6.25 -6.734375 "/>
</symbol>
<symbol overflow="visible" id="glyph0-2">
<path style="stroke:none;" d="M 1.203125 -9.71875 L 2.359375 -9.71875 L 2.359375 0 L 1.203125 0 Z M 1.203125 -9.71875 "/>
</symbol>
<symbol overflow="visible" id="glyph0-3">
<path style="stroke:none;" d="M 1.203125 -7 L 2.359375 -7 L 2.359375 0 L 1.203125 0 Z M 1.203125 -9.71875 L 2.359375 -9.71875 L 2.359375 -8.265625 L 1.203125 -8.265625 Z M 1.203125 -9.71875 "/>
</symbol>
<symbol overflow="visible" id="glyph0-4">
<path style="stroke:none;" d="M 7.1875 -3.78125 L 7.1875 -3.21875 L 1.90625 -3.21875 C 1.957031 -2.425781 2.195312 -1.820312 2.625 -1.40625 C 3.050781 -1 3.644531 -0.796875 4.40625 -0.796875 C 4.84375 -0.796875 5.269531 -0.847656 5.6875 -0.953125 C 6.101562 -1.066406 6.515625 -1.226562 6.921875 -1.4375 L 6.921875 -0.359375 C 6.515625 -0.179688 6.09375 -0.046875 5.65625 0.046875 C 5.21875 0.140625 4.78125 0.1875 4.34375 0.1875 C 3.21875 0.1875 2.328125 -0.132812 1.671875 -0.78125 C 1.023438 -1.4375 0.703125 -2.320312 0.703125 -3.4375 C 0.703125 -4.582031 1.007812 -5.488281 1.625 -6.15625 C 2.25 -6.832031 3.085938 -7.171875 4.140625 -7.171875 C 5.078125 -7.171875 5.816406 -6.863281 6.359375 -6.25 C 6.910156 -5.644531 7.1875 -4.820312 7.1875 -3.78125 Z M 6.046875 -4.125 C 6.035156 -4.75 5.859375 -5.25 5.515625 -5.625 C 5.171875 -6 4.71875 -6.1875 4.15625 -6.1875 C 3.507812 -6.1875 2.992188 -6.003906 2.609375 -5.640625 C 2.222656 -5.285156 2 -4.78125 1.9375 -4.125 Z M 6.046875 -4.125 "/>
</symbol>
<symbol overflow="visible" id="glyph0-5">
<path style="stroke:none;" d="M 7.015625 -4.21875 L 7.015625 0 L 5.875 0 L 5.875 -4.1875 C 5.875 -4.851562 5.742188 -5.347656 5.484375 -5.671875 C 5.222656 -6.003906 4.835938 -6.171875 4.328125 -6.171875 C 3.703125 -6.171875 3.207031 -5.972656 2.84375 -5.578125 C 2.488281 -5.179688 2.3125 -4.640625 2.3125 -3.953125 L 2.3125 0 L 1.15625 0 L 1.15625 -7 L 2.3125 -7 L 2.3125 -5.90625 C 2.59375 -6.332031 2.914062 -6.648438 3.28125 -6.859375 C 3.65625 -7.066406 4.085938 -7.171875 4.578125 -7.171875 C 5.378906 -7.171875 5.984375 -6.921875 6.390625 -6.421875 C 6.804688 -5.921875 7.015625 -5.1875 7.015625 -4.21875 Z M 7.015625 -4.21875 "/>
</symbol>
<symbol overflow="visible" id="glyph0-6">
<path style="stroke:none;" d="M 2.34375 -8.984375 L 2.34375 -7 L 4.71875 -7 L 4.71875 -6.109375 L 2.34375 -6.109375 L 2.34375 -2.3125 C 2.34375 -1.738281 2.421875 -1.367188 2.578125 -1.203125 C 2.734375 -1.046875 3.050781 -0.96875 3.53125 -0.96875 L 4.71875 -0.96875 L 4.71875 0 L 3.53125 0 C 2.644531 0 2.03125 -0.164062 1.6875 -0.5 C 1.351562 -0.832031 1.1875 -1.4375 1.1875 -2.3125 L 1.1875 -6.109375 L 0.34375 -6.109375 L 0.34375 -7 L 1.1875 -7 L 1.1875 -8.984375 Z M 2.34375 -8.984375 "/>
</symbol>
<symbol overflow="visible" id="glyph0-7">
<path style="stroke:none;" d="M 5.265625 -5.921875 C 5.128906 -5.992188 4.984375 -6.046875 4.828125 -6.078125 C 4.679688 -6.117188 4.519531 -6.140625 4.34375 -6.140625 C 3.6875 -6.140625 3.179688 -5.925781 2.828125 -5.5 C 2.484375 -5.082031 2.3125 -4.476562 2.3125 -3.6875 L 2.3125 0 L 1.15625 0 L 1.15625 -7 L 2.3125 -7 L 2.3125 -5.90625 C 2.5625 -6.332031 2.878906 -6.648438 3.265625 -6.859375 C 3.648438 -7.066406 4.117188 -7.171875 4.671875 -7.171875 C 4.753906 -7.171875 4.84375 -7.164062 4.9375 -7.15625 C 5.03125 -7.144531 5.132812 -7.128906 5.25 -7.109375 Z M 5.265625 -5.921875 "/>
</symbol>
<symbol overflow="visible" id="glyph0-8">
<path style="stroke:none;" d="M 5.671875 -6.796875 L 5.671875 -5.703125 C 5.347656 -5.867188 5.007812 -5.992188 4.65625 -6.078125 C 4.300781 -6.160156 3.9375 -6.203125 3.5625 -6.203125 C 3 -6.203125 2.570312 -6.113281 2.28125 -5.9375 C 2 -5.769531 1.859375 -5.507812 1.859375 -5.15625 C 1.859375 -4.882812 1.957031 -4.671875 2.15625 -4.515625 C 2.363281 -4.367188 2.773438 -4.226562 3.390625 -4.09375 L 3.78125 -4 C 4.601562 -3.832031 5.1875 -3.585938 5.53125 -3.265625 C 5.875 -2.941406 6.046875 -2.5 6.046875 -1.9375 C 6.046875 -1.28125 5.785156 -0.757812 5.265625 -0.375 C 4.753906 0 4.050781 0.1875 3.15625 0.1875 C 2.78125 0.1875 2.390625 0.148438 1.984375 0.078125 C 1.578125 0.00390625 1.144531 -0.101562 0.6875 -0.25 L 0.6875 -1.4375 C 1.113281 -1.21875 1.53125 -1.050781 1.9375 -0.9375 C 2.351562 -0.832031 2.765625 -0.78125 3.171875 -0.78125 C 3.710938 -0.78125 4.128906 -0.875 4.421875 -1.0625 C 4.710938 -1.25 4.859375 -1.507812 4.859375 -1.84375 C 4.859375 -2.15625 4.753906 -2.394531 4.546875 -2.5625 C 4.335938 -2.726562 3.875 -2.890625 3.15625 -3.046875 L 2.765625 -3.140625 C 2.046875 -3.285156 1.53125 -3.515625 1.21875 -3.828125 C 0.90625 -4.140625 0.75 -4.566406 0.75 -5.109375 C 0.75 -5.765625 0.976562 -6.269531 1.4375 -6.625 C 1.90625 -6.988281 2.570312 -7.171875 3.4375 -7.171875 C 3.851562 -7.171875 4.25 -7.140625 4.625 -7.078125 C 5 -7.015625 5.347656 -6.921875 5.671875 -6.796875 Z M 5.671875 -6.796875 "/>
</symbol>
<symbol overflow="visible" id="glyph0-9">
<path style="stroke:none;" d="M 4.125 0.65625 C 3.789062 1.488281 3.46875 2.03125 3.15625 2.28125 C 2.851562 2.53125 2.445312 2.65625 1.9375 2.65625 L 1.015625 2.65625 L 1.015625 1.703125 L 1.6875 1.703125 C 2 1.703125 2.242188 1.625 2.421875 1.46875 C 2.597656 1.320312 2.789062 0.96875 3 0.40625 L 3.21875 -0.109375 L 0.375 -7 L 1.59375 -7 L 3.78125 -1.53125 L 5.96875 -7 L 7.1875 -7 Z M 4.125 0.65625 "/>
</symbol>
<symbol overflow="visible" id="glyph0-10">
<path style="stroke:none;" d="M 1.25 -9.328125 L 2.953125 -9.328125 L 7.09375 -1.53125 L 7.09375 -9.328125 L 8.3125 -9.328125 L 8.3125 0 L 6.609375 0 L 2.484375 -7.796875 L 2.484375 0 L 1.25 0 Z M 1.25 -9.328125 "/>
</symbol>
<symbol overflow="visible" id="glyph0-11">
<path style="stroke:none;" d="M 1.25 -9.328125 L 6.609375 -9.328125 L 6.609375 -8.265625 L 2.515625 -8.265625 L 2.515625 -5.515625 L 6.21875 -5.515625 L 6.21875 -4.453125 L 2.515625 -4.453125 L 2.515625 0 L 1.25 0 Z M 1.25 -9.328125 "/>
</symbol>
<symbol overflow="visible" id="glyph0-12">
<path style="stroke:none;" d="M 6.84375 -9.015625 L 6.84375 -7.796875 C 6.363281 -8.023438 5.910156 -8.191406 5.484375 -8.296875 C 5.066406 -8.410156 4.660156 -8.46875 4.265625 -8.46875 C 3.578125 -8.46875 3.046875 -8.332031 2.671875 -8.0625 C 2.296875 -7.800781 2.109375 -7.425781 2.109375 -6.9375 C 2.109375 -6.519531 2.234375 -6.207031 2.484375 -6 C 2.734375 -5.789062 3.203125 -5.625 3.890625 -5.5 L 4.65625 -5.34375 C 5.59375 -5.15625 6.285156 -4.835938 6.734375 -4.390625 C 7.179688 -3.941406 7.40625 -3.335938 7.40625 -2.578125 C 7.40625 -1.671875 7.101562 -0.984375 6.5 -0.515625 C 5.894531 -0.046875 5.007812 0.1875 3.84375 0.1875 C 3.394531 0.1875 2.921875 0.132812 2.421875 0.03125 C 1.929688 -0.0703125 1.414062 -0.21875 0.875 -0.40625 L 0.875 -1.71875 C 1.394531 -1.425781 1.898438 -1.207031 2.390625 -1.0625 C 2.878906 -0.914062 3.363281 -0.84375 3.84375 -0.84375 C 4.5625 -0.84375 5.113281 -0.984375 5.5 -1.265625 C 5.894531 -1.546875 6.09375 -1.953125 6.09375 -2.484375 C 6.09375 -2.941406 5.953125 -3.296875 5.671875 -3.546875 C 5.390625 -3.804688 4.925781 -4.003906 4.28125 -4.140625 L 3.515625 -4.28125 C 2.578125 -4.46875 1.894531 -4.757812 1.46875 -5.15625 C 1.050781 -5.5625 0.84375 -6.117188 0.84375 -6.828125 C 0.84375 -7.660156 1.132812 -8.3125 1.71875 -8.78125 C 2.300781 -9.257812 3.101562 -9.5 4.125 -9.5 C 4.5625 -9.5 5.003906 -9.457031 5.453125 -9.375 C 5.910156 -9.300781 6.375 -9.179688 6.84375 -9.015625 Z M 6.84375 -9.015625 "/>
</symbol>
<symbol overflow="visible" id="glyph0-13">
<path style="stroke:none;" d="M 3.921875 -6.1875 C 3.304688 -6.1875 2.816406 -5.945312 2.453125 -5.46875 C 2.097656 -4.988281 1.921875 -4.332031 1.921875 -3.5 C 1.921875 -2.65625 2.097656 -1.992188 2.453125 -1.515625 C 2.804688 -1.035156 3.296875 -0.796875 3.921875 -0.796875 C 4.535156 -0.796875 5.019531 -1.035156 5.375 -1.515625 C 5.726562 -2.003906 5.90625 -2.664062 5.90625 -3.5 C 5.90625 -4.320312 5.726562 -4.972656 5.375 -5.453125 C 5.019531 -5.941406 4.535156 -6.1875 3.921875 -6.1875 Z M 3.921875 -7.171875 C 4.921875 -7.171875 5.703125 -6.84375 6.265625 -6.1875 C 6.835938 -5.539062 7.125 -4.644531 7.125 -3.5 C 7.125 -2.351562 6.835938 -1.453125 6.265625 -0.796875 C 5.703125 -0.140625 4.921875 0.1875 3.921875 0.1875 C 2.910156 0.1875 2.117188 -0.140625 1.546875 -0.796875 C 0.984375 -1.453125 0.703125 -2.351562 0.703125 -3.5 C 0.703125 -4.644531 0.984375 -5.539062 1.546875 -6.1875 C 2.117188 -6.84375 2.910156 -7.171875 3.921875 -7.171875 Z M 3.921875 -7.171875 "/>
</symbol>
<symbol overflow="visible" id="glyph0-14">
<path style="stroke:none;" d="M 5.8125 -5.9375 L 5.8125 -9.71875 L 6.953125 -9.71875 L 6.953125 0 L 5.8125 0 L 5.8125 -1.046875 C 5.570312 -0.628906 5.265625 -0.316406 4.890625 -0.109375 C 4.523438 0.0859375 4.082031 0.1875 3.5625 0.1875 C 2.71875 0.1875 2.03125 -0.148438 1.5 -0.828125 C 0.96875 -1.503906 0.703125 -2.394531 0.703125 -3.5 C 0.703125 -4.59375 0.96875 -5.476562 1.5 -6.15625 C 2.03125 -6.832031 2.71875 -7.171875 3.5625 -7.171875 C 4.082031 -7.171875 4.523438 -7.066406 4.890625 -6.859375 C 5.265625 -6.660156 5.570312 -6.351562 5.8125 -5.9375 Z M 1.890625 -3.5 C 1.890625 -2.644531 2.0625 -1.976562 2.40625 -1.5 C 2.757812 -1.019531 3.238281 -0.78125 3.84375 -0.78125 C 4.457031 -0.78125 4.9375 -1.019531 5.28125 -1.5 C 5.632812 -1.976562 5.8125 -2.644531 5.8125 -3.5 C 5.8125 -4.34375 5.632812 -5.003906 5.28125 -5.484375 C 4.9375 -5.960938 4.457031 -6.203125 3.84375 -6.203125 C 3.238281 -6.203125 2.757812 -5.960938 2.40625 -5.484375 C 2.0625 -5.003906 1.890625 -4.34375 1.890625 -3.5 Z M 1.890625 -3.5 "/>
</symbol>
<symbol overflow="visible" id="glyph0-15">
<path style="stroke:none;" d="M 3.25 -9.328125 L 4.3125 -9.328125 L 1.0625 1.1875 L 0 1.1875 Z M 3.25 -9.328125 "/>
</symbol>
<symbol overflow="visible" id="glyph0-16">
<path style="stroke:none;" d="M 1.25 -9.328125 L 2.515625 -9.328125 L 2.515625 -5.5 L 7.109375 -5.5 L 7.109375 -9.328125 L 8.359375 -9.328125 L 8.359375 0 L 7.109375 0 L 7.109375 -4.4375 L 2.515625 -4.4375 L 2.515625 0 L 1.25 0 Z M 1.25 -9.328125 "/>
</symbol>
<symbol overflow="visible" id="glyph0-17">
<path style="stroke:none;" d="M 4.390625 -3.515625 C 3.460938 -3.515625 2.816406 -3.40625 2.453125 -3.1875 C 2.097656 -2.976562 1.921875 -2.617188 1.921875 -2.109375 C 1.921875 -1.703125 2.050781 -1.378906 2.3125 -1.140625 C 2.582031 -0.898438 2.953125 -0.78125 3.421875 -0.78125 C 4.054688 -0.78125 4.566406 -1.003906 4.953125 -1.453125 C 5.335938 -1.910156 5.53125 -2.515625 5.53125 -3.265625 L 5.53125 -3.515625 Z M 6.671875 -4 L 6.671875 0 L 5.53125 0 L 5.53125 -1.0625 C 5.269531 -0.632812 4.941406 -0.316406 4.546875 -0.109375 C 4.160156 0.0859375 3.679688 0.1875 3.109375 0.1875 C 2.390625 0.1875 1.816406 -0.015625 1.390625 -0.421875 C 0.972656 -0.828125 0.765625 -1.363281 0.765625 -2.03125 C 0.765625 -2.820312 1.023438 -3.414062 1.546875 -3.8125 C 2.078125 -4.21875 2.867188 -4.421875 3.921875 -4.421875 L 5.53125 -4.421875 L 5.53125 -4.53125 C 5.53125 -5.0625 5.351562 -5.46875 5 -5.75 C 4.65625 -6.039062 4.171875 -6.1875 3.546875 -6.1875 C 3.140625 -6.1875 2.75 -6.140625 2.375 -6.046875 C 2 -5.953125 1.632812 -5.8125 1.28125 -5.625 L 1.28125 -6.671875 C 1.695312 -6.835938 2.101562 -6.960938 2.5 -7.046875 C 2.894531 -7.128906 3.28125 -7.171875 3.65625 -7.171875 C 4.675781 -7.171875 5.429688 -6.90625 5.921875 -6.375 C 6.421875 -5.851562 6.671875 -5.0625 6.671875 -4 Z M 6.671875 -4 "/>
</symbol>
<symbol overflow="visible" id="glyph0-18">
<path style="stroke:none;" d="M 5.8125 -3.578125 C 5.8125 -4.410156 5.640625 -5.054688 5.296875 -5.515625 C 4.953125 -5.972656 4.46875 -6.203125 3.84375 -6.203125 C 3.226562 -6.203125 2.75 -5.972656 2.40625 -5.515625 C 2.0625 -5.054688 1.890625 -4.410156 1.890625 -3.578125 C 1.890625 -2.753906 2.0625 -2.113281 2.40625 -1.65625 C 2.75 -1.195312 3.226562 -0.96875 3.84375 -0.96875 C 4.46875 -0.96875 4.953125 -1.195312 5.296875 -1.65625 C 5.640625 -2.113281 5.8125 -2.753906 5.8125 -3.578125 Z M 6.953125 -0.875 C 6.953125 0.320312 6.6875 1.207031 6.15625 1.78125 C 5.632812 2.363281 4.828125 2.65625 3.734375 2.65625 C 3.328125 2.65625 2.945312 2.625 2.59375 2.5625 C 2.238281 2.507812 1.890625 2.421875 1.546875 2.296875 L 1.546875 1.171875 C 1.890625 1.359375 2.222656 1.492188 2.546875 1.578125 C 2.878906 1.671875 3.21875 1.71875 3.5625 1.71875 C 4.3125 1.71875 4.875 1.519531 5.25 1.125 C 5.625 0.726562 5.8125 0.132812 5.8125 -0.65625 L 5.8125 -1.234375 C 5.570312 -0.816406 5.265625 -0.503906 4.890625 -0.296875 C 4.523438 -0.0976562 4.082031 0 3.5625 0 C 2.707031 0 2.015625 -0.328125 1.484375 -0.984375 C 0.960938 -1.640625 0.703125 -2.503906 0.703125 -3.578125 C 0.703125 -4.660156 0.960938 -5.53125 1.484375 -6.1875 C 2.015625 -6.84375 2.707031 -7.171875 3.5625 -7.171875 C 4.082031 -7.171875 4.523438 -7.066406 4.890625 -6.859375 C 5.265625 -6.648438 5.570312 -6.34375 5.8125 -5.9375 L 5.8125 -7 L 6.953125 -7 Z M 6.953125 -0.875 "/>
</symbol>
<symbol overflow="visible" id="glyph0-19">
<path style="stroke:none;" d="M 1.59375 -1.0625 L 3.65625 -1.0625 L 3.65625 -8.171875 L 1.40625 -7.734375 L 1.40625 -8.875 L 3.640625 -9.328125 L 4.90625 -9.328125 L 4.90625 -1.0625 L 6.953125 -1.0625 L 6.953125 0 L 1.59375 0 Z M 1.59375 -1.0625 "/>
</symbol>
<symbol overflow="visible" id="glyph0-20">
<path style="stroke:none;" d="M 2.453125 -1.0625 L 6.859375 -1.0625 L 6.859375 0 L 0.9375 0 L 0.9375 -1.0625 C 1.414062 -1.5625 2.066406 -2.226562 2.890625 -3.0625 C 3.722656 -3.894531 4.242188 -4.429688 4.453125 -4.671875 C 4.859375 -5.128906 5.140625 -5.515625 5.296875 -5.828125 C 5.460938 -6.140625 5.546875 -6.445312 5.546875 -6.75 C 5.546875 -7.25 5.367188 -7.65625 5.015625 -7.96875 C 4.671875 -8.28125 4.21875 -8.4375 3.65625 -8.4375 C 3.257812 -8.4375 2.84375 -8.363281 2.40625 -8.21875 C 1.96875 -8.082031 1.5 -7.878906 1 -7.609375 L 1 -8.875 C 1.507812 -9.082031 1.984375 -9.238281 2.421875 -9.34375 C 2.867188 -9.445312 3.273438 -9.5 3.640625 -9.5 C 4.609375 -9.5 5.378906 -9.253906 5.953125 -8.765625 C 6.523438 -8.285156 6.8125 -7.640625 6.8125 -6.828125 C 6.8125 -6.453125 6.738281 -6.09375 6.59375 -5.75 C 6.445312 -5.40625 6.1875 -5 5.8125 -4.53125 C 5.707031 -4.40625 5.375 -4.054688 4.8125 -3.484375 C 4.257812 -2.910156 3.472656 -2.101562 2.453125 -1.0625 Z M 2.453125 -1.0625 "/>
</symbol>
<symbol overflow="visible" id="glyph0-21">
<path style="stroke:none;" d="M 5.1875 -5.03125 C 5.789062 -4.90625 6.257812 -4.632812 6.59375 -4.21875 C 6.9375 -3.8125 7.109375 -3.3125 7.109375 -2.71875 C 7.109375 -1.789062 6.789062 -1.070312 6.15625 -0.5625 C 5.53125 -0.0625 4.632812 0.1875 3.46875 0.1875 C 3.070312 0.1875 2.664062 0.144531 2.25 0.0625 C 1.84375 -0.0078125 1.414062 -0.125 0.96875 -0.28125 L 0.96875 -1.5 C 1.320312 -1.289062 1.707031 -1.132812 2.125 -1.03125 C 2.539062 -0.925781 2.976562 -0.875 3.4375 -0.875 C 4.226562 -0.875 4.828125 -1.03125 5.234375 -1.34375 C 5.648438 -1.65625 5.859375 -2.113281 5.859375 -2.71875 C 5.859375 -3.257812 5.664062 -3.6875 5.28125 -4 C 4.894531 -4.3125 4.359375 -4.46875 3.671875 -4.46875 L 2.59375 -4.46875 L 2.59375 -5.5 L 3.71875 -5.5 C 4.34375 -5.5 4.816406 -5.625 5.140625 -5.875 C 5.472656 -6.125 5.640625 -6.484375 5.640625 -6.953125 C 5.640625 -7.429688 5.46875 -7.796875 5.125 -8.046875 C 4.789062 -8.304688 4.304688 -8.4375 3.671875 -8.4375 C 3.328125 -8.4375 2.957031 -8.394531 2.5625 -8.3125 C 2.164062 -8.238281 1.726562 -8.125 1.25 -7.96875 L 1.25 -9.09375 C 1.726562 -9.226562 2.175781 -9.328125 2.59375 -9.390625 C 3.019531 -9.460938 3.414062 -9.5 3.78125 -9.5 C 4.738281 -9.5 5.492188 -9.28125 6.046875 -8.84375 C 6.609375 -8.40625 6.890625 -7.816406 6.890625 -7.078125 C 6.890625 -6.566406 6.742188 -6.128906 6.453125 -5.765625 C 6.160156 -5.410156 5.738281 -5.164062 5.1875 -5.03125 Z M 5.1875 -5.03125 "/>
</symbol>
</g>
</defs>
<g id="surface57962">
<rect x="0" y="0" width="487" height="511" style="fill:rgb(100%,100%,100%);fill-opacity:1;stroke:none;"/>
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(100%,100%,100%);fill-opacity:1;" d="M 3 410.460938 L 83.648438 410.460938 L 83.648438 481.621094 L 3 481.621094 Z M 3 410.460938 "/>
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(100%,100%,100%);fill-opacity:1;" d="M 83.648438 481.621094 C 83.648438 488.171875 65.597656 493.480469 43.324219 493.480469 C 21.054688 493.480469 3 488.171875 3 481.621094 C 3 475.070312 21.054688 469.761719 43.324219 469.761719 C 65.597656 469.761719 83.648438 475.070312 83.648438 481.621094 "/>
<path style="fill-rule:evenodd;fill:rgb(100%,100%,100%);fill-opacity:1;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 5.082506 8.493087 C 5.082506 8.820626 4.179967 9.086055 3.066295 9.086055 C 1.952818 9.086055 1.050084 8.820626 1.050084 8.493087 C 1.050084 8.165548 1.952818 7.900118 3.066295 7.900118 C 4.179967 7.900118 5.082506 8.165548 5.082506 8.493087 "