Marvin Preuss xsteadfastx photo

github twitter mastodon flickr

linux

Jitsi-Meet in Corana Zeiten

/// d9d5107 /// jitsi communication voip linux docker ansible golang covid19 prometheus raspberrypi

Social Distancing gehört eigentlich zu meinen Stärken. Soziale Kontakte kostet mich einen riesigen Aufwand. Auf einmal wurden wir alle Zuhausebleiber zu Helden der Corona-Zeit. Ich hätte es nicht gedacht, diese Distanz tat mir nicht gut. Ich scrollte durch viele “witzige” Screenshots von Gruppenvideochats. Vor ein paar Jahren probiere ich mal Jitsi-Meet aus. Ein kleiner Testlauf für die Firma, mit mäßigen Erfolg. Ab drei Teilnehmern ging der Server in die Knie. Nachdem ich diesen Bugreport sah, wurde mich auch klar wieso: Es gibt ein Firefox Bug, der die Verbindungen stark einschränkt, und das für alle Beteiligen.

Social Distancing gehört eigentlich zu meinen Stärken. Soziale Kontakte kostet mich einen riesigen Aufwand. Auf einmal wurden wir alle Zuhausebleiber zu Helden der Corona-Zeit. Ich hätte es nicht gedacht, diese Distanz tat mir nicht gut. Ich scrollte durch viele “witzige” Screenshots von Gruppenvideochats.

Vor ein paar Jahren probiere ich mal Jitsi-Meet aus. Ein kleiner Testlauf für die Firma, mit mäßigen Erfolg. Ab drei Teilnehmern ging der Server in die Knie. Nachdem ich diesen Bugreport sah, wurde mich auch klar wieso: Es gibt ein Firefox Bug, der die Verbindungen stark einschränkt, und das für alle Beteiligen. Egal. Ich kannte Jitsi schon aus meinen XMPP-Hype Jahren. Damals noch als Java Client der auch schon Audio und Video kannte. Er nutze dabei XMPP Jingle für das Aushandeln der Verbindungen. Da ich keine Lust darauf habe kommerzielle, closed Source Geschichten zu benutzen (aus Überzeugung), wollte ich etwas eigenes Aufsetzen. Jitsi musste wieder her halten.

In meiner Fantasie als Mega Super Admin rolle ich gleich mehrere Instanzen aus. Aus diesem Grund muss es eine Ansible Rolle sein. Diese wäre Teil meiner persöhnlichen Infrastruktausrollung. Ich schlug mich durch bereits vorhandene Ansible Rollen. Ein scharzer Tag innerhalb schwarzer Tage. Ich setze immer wieder frische virtualle Hetzner Server auf. Egal ob Debian oder Ubuntu, immer bekam ich andere Fehler. Anscheinend baut Jitsi intern viel um und will nun NGINX als neuen als Reverseproxy nutzen. Zumindestens nehme ich diese Aussage als Ausrede meines Nichtkönnens. Es gab aber noch eine andere Alternative: das docker-compose Setup. Aufgesetzt, funktioniert. Wieso also der Hassle?

Meine Ansible Rolle setzt ein funktionierendes Docker Setup voraus. Dann als Ansible Variabel den Host setzen:

    jitsi_meet__host: foo.bar.tld

Die meiste Arbeit steckt in dem offiziellen docker-compose File. Es kümmert sich sogar um letsencrypt SSL Zertifikate. Ich klickte mir erstmal den billigsten vServer in der Hetzner-Cloud. Natürlich muss ich auch mitbekommen wie sich die Zahlen der Konferenzen und User auf den Ressourcen-Verbrauch auswirkt. Ich brauchte einen Prometheus Exporter. Auch hier gab es was, dies beinahaltet Gefummel am XMPP Server. Keine Lust darauf wenn ich sowas automatisiert ausrollen möchte. Und da ich gerade eh mit go rumspiele musste ich es einfach selber machen. Stellt sich raus, die Videobridge Komponente in Jitsi kann Statistiken. Ich nehme die angebotenen Werte und baue daraus Prometheus Metriken. Ein Hack. Aber es macht was es soll. Der jitsiexporter!

Nun treffen wir uns einmal Morgends, frühstücken zusammen. Ich habe soviel soziale Kontakte wie noch nie. Diese Zeiten erzeugen eine Sensucht nach Nähe. Dies ist priviligiertes Gejammer aus der Quarantäne, das ist mir bewusst.



Und da war mein Caps Lock wieder Caps Lock

/// f5b3ad6 /// linux systemd capslock

Ich habe über Umwegen eine ziemlich schöne, kleine CHERRY Tastertur (ML4400) zugesteckt bekommen. Eins der Vorteile ist in manchen Fällen auch ein Nachteil: Es gibt keine Windows Taste. Nichts das ich jemals stolz darauf war diese auf meiner Tastertur zu haben. Ich erinnere mich daran das man für CHERRY Tasterturen sogar einen Ersatz mit TUX bekam. Nun nutze ich seit vielen vielen Jahren i3 als WindowManager und nutze die Windows Taste um viele Shortcuts darin auszuführen.

Ich habe über Umwegen eine ziemlich schöne, kleine CHERRY Tastertur (ML4400) zugesteckt bekommen. Eins der Vorteile ist in manchen Fällen auch ein Nachteil: Es gibt keine Windows Taste. Nichts das ich jemals stolz darauf war diese auf meiner Tastertur zu haben. Ich erinnere mich daran das man für CHERRY Tasterturen sogar einen Ersatz mit TUX bekam. Nun nutze ich seit vielen vielen Jahren i3 als WindowManager und nutze die Windows Taste um viele Shortcuts darin auszuführen. Mein Musclememory ist komplett darauf konditioniert. Will ich meine neue Tastertur also benutzen, muss ich in irgendeine Richtung umdenken. Caps Lock wollte ich eh schon immer mal wegmappen. Also was solls.

    setxkbmap -option caps:super

Funktioniert bis auf mein halbes Abbrechen meiner Finger und dem Entgegenarbeiten des gut trainierten Muskelspeichers. Ich fing an zu arbeiten, klappte irgendwann den Laptop zu und am nächsten Tag wunderte ich mich, dass nichts mehr so wahr wie ich es wollte. Kommt heraus: Nachdem Suspend sind die Einstellungen weg. Wenig rumgegoogelt und auf was gestoßen. Es gibt das Verzeichnis /lib/systemd/system-sleep. Darin kann man Scripte stecken die beim Einschlafen oder Aufwachen ausgeführt werden. Dabei übergibt systemd zwei Argumente bei jedem Suspend: 1. pre oder post und 2. die Action sowie suspend oder hibernate. Mein Script sieht wie folgt aus:

        #!/bin/sh
        case $1/$2 in
            pre/*)
                echo "Going to $2..."
                ;;
            post/*)
                setxkbmap -option super:caps
                ;;
        esac

Wichtig wäre es vielleicht auch wo genau das Script rein kommt. Dies findet man schnell mit systemctl help systemd-suspend.service heraus.

Update: Manchmal (und das gefühlt random) wird Caps Lock dann doch wieder Caps Lock. Nun greife ich zu härteren Bandagen: /etc/default/keyboard:

    # KEYBOARD CONFIGURATION FILE

    # Consult the keyboard(5) manual page.

    XKBMODEL="pc105"
    XKBLAYOUT="de"
    XKBVARIANT=""
    XKBOPTIONS="caps:super"

    BACKSPACE="guess"

Danach einmal dpkg-reconfigure keyboard-configuration ausführen!



fortlit: Zeit und Literatur

/// d794cd8 /// linux literature books code python

Ich bin auf einen Blogartikel zu einer Kindle basierten Uhr gestossen. Darin beschreibt Jaap Meijers wie er zu jeder Uhrzeit ein Zitat aus der Literatur auf dem Kindle darstellt. Er nutzt dazu Daten des Guardians, die Zeiten aus Büchern sammeln ließen. Sein Projekt inspirierte JohannesNE zu einer Webversion. Ich wieder rum wollte dies nutzen um mir bei jedem Shell Aufruf mir das passende Zitat zu der aktuellen Zeit anzeigen zu lassen.

Ich bin auf einen Blogartikel zu einer Kindle basierten Uhr gestossen. Darin beschreibt Jaap Meijers wie er zu jeder Uhrzeit ein Zitat aus der Literatur auf dem Kindle darstellt. Er nutzt dazu Daten des Guardians, die Zeiten aus Büchern sammeln ließen. Sein Projekt inspirierte JohannesNE zu einer Webversion. Ich wieder rum wollte dies nutzen um mir bei jedem Shell Aufruf mir das passende Zitat zu der aktuellen Zeit anzeigen zu lassen. Daraus entstand fortlit. Ein kleines Python Script, welches man in die Shell seiner Wahl einbauen kann. Ich habe die Daten ein wenig gesäubert und ein schönes JSON daraus gebaut. Für die Einfachheit gibt es auch ein PEX-File. Viel Spaß!

Der Name ist eine Anlehnung an das kleine Programm Fortune.

If I was punctual in quitting Mlle. Reuter's domicile, I was at least equally punctual in arriving there; I came the next day at five minutes before two, and on reaching the schoolroom door, before I opened it, I heard a rapid, gabbling sound, which warned me that the "priere du midi" was not yet concluded.
- The Professor, Charlotte Brontë


Python Programme ausrollen mit PEX

/// d794cd8 /// python linux

Python hat mit vielen Vorurteilen zu kämpfen. Es sei langsam, nur komisches Gescripte, entweder zu dynamisch oder nicht dynamisch genug. Ein, zum Teil, verständlicher Vorwurf ist die Schwierigkeit des Ausrollens und Veröffentlichung von Paketen. Dem möchte ich nur zum Teil zustimmen. Als völliger Programmier-Noob tue ich es mir ab und zu wirklich schwer eine gute setup.py zu schreiben. Und muss ich dies tun, suche ich mich durch die verschiedensten Auswüchse meiner liebsten Python-Projekte auf GitHub.

Python hat mit vielen Vorurteilen zu kämpfen. Es sei langsam, nur komisches Gescripte, entweder zu dynamisch oder nicht dynamisch genug. Ein, zum Teil, verständlicher Vorwurf ist die Schwierigkeit des Ausrollens und Veröffentlichung von Paketen. Dem möchte ich nur zum Teil zustimmen. Als völliger Programmier-Noob tue ich es mir ab und zu wirklich schwer eine gute setup.py zu schreiben. Und muss ich dies tun, suche ich mich durch die verschiedensten Auswüchse meiner liebsten Python-Projekte auf GitHub. Was mir da immer hilft, ist das Beispiel Projekt. Daran kann man sich wunderbar entlang hangeln. Dann steht das nächste Problem an. Wie veröffentliche ich das ganze am besten. Einerseits lädt man es bei PyPI hoch. Auf der Benutzerseite fragt man sich jedesmal wie man das Paket am besten installieren. Man möchte niemals pip install als root ausführen und das Paket einfach blind in den globalen Raum installieren. Es gibt pip install --user foobar. Dies installiert es zwar global aber nur im eigenen Home-Verzeichnis. Auch nicht so toll und es kann natürlich zu Abhängigkeitesproblemen kommen, so sehr diese Raum mit anderen Paketen wächst und immer mehr verwulstet. Zum entwickeln benutzt man immer virtualenvs um sich seine abgeschlossenen Umgebungen zu bauen. Als Anwender ist das auch ziemlich unschön diese zu managen und am Ende verliert man dann doch den Überblich. Es gibt da pipsi. Dieses kleine Tool nimmt das Umgebungs-Management in die Hand. Funktioniert ganz wunderbar. Doch noch gibt es ganz andere Problemherde. Was ist mit Abhängigkeiten die ein Kompilieren nötig haben? Immer mehr Sachen müssen installiert werden und am Ende klappt es zwar irgendwie, schön ist es aber nicht. Gerade wenn das Paket von normalen Endnutzern benutzt werden soll.

Neben Tools wie pyinstaller gibt es, das von Twitter entwickelte, PEX. Dies macht sich zu eigen das Python Module aus Zip-Files importieren kann und Python wohl ziemlich vergibt was die Struktur von Zip-Files anbelangt. pex bastelt ein virtualenv, zippt es und knallt einen Shebang vor das Zip. Nun ist es ausführbar und man benötigt nur noch einen passenden Python Interpreter, alles was zum ausführen gebraucht wird, befindet sich in dem Zip. Dies ist ein anderer Ansatz als Python und seine Abhängigkeiten in ein Gesammtpaket zu schnüren. Python wird weiterhin auf dem System gebraucht. pex unterstützt sogar mehrere Python Versionen und Plattformen in einem PEX-File.

Ich habe das ganze mal für mein kleines Tool DoTheBackup gemacht:

pex -e dothebackup.ui:main --python=python3.6 --python=python3.5 --python=python3.4 --python-shebang=/usr/bin/python3 -o dist/dothebackup-`uname -s`-`uname -m`.pex --no-wheel --disable-cache -v .
  • -e dothebackup.ui:main: Dies ist der Entrypoint. Also die Funktion die ausgeführt wird, wenn das Programm ausgeführt wird. In diesem Fall eine click Funktion.
  • --python=python3.6: Hier beschreibt man die Python Version für die das File gebaut wird. Das schöne: Man kann mehrere angeben.
  • --python-shebang=/usr/bin/python3: Wir wollen es so universell halten wie möglich. Standardmäßig setzt pex hier die volle Version ein: /usr/bin/python3.6. Dies bringt uns aber nichts wenn es auch auf anderen Versionen laufen soll. /usr/bin/python3 should do the trick.
  • -o dist/dothebackup-uname -s-uname -m.pex: Dies beschreibt das Outputfile. In diesem Fall: dothebackup-Linux-x86_64.pex.
  • --no-wheel: Dies habe ich gebraucht wegen irgendeinen Fehlers. Er benutzt zum bauen keine wheels.
  • --disable-cache: pip benutzt keine Pakete aus dem Cache.
  • -v: Verbose.
  • .: Die Location.. also das aktuelle Verzeichnis.

Das bauen des PEX-Files lasse ich von Travis machen. Dies in meinem ultimativen Python Docker Image. Alles dazu findet ihr in dem Repo.

Hier noch ein kleines Video das PEX erklärt:



Linux Audio an UPNP und Chromecast

/// d794cd8 /// linux

Ich sitze krank zuhause. Was stellt man an? Der Kopf brummt und das so laut und intensiv, dass man am besten, natürlich kontraproduktiv, an seinem Linux-Audio-Setup rumschraubt. Oder ist dies der Grund für mein Unbehagen? Was kann denn da schon schiefgehen? Ich starte mal mit einem kleinen Schaubild meines Setups. +------------------------+ | | | | +----------------------+ +----v-----+ | | firefox/mpv/... | | | | | through pulseaudio | +---------+ client +------------+ | +------------+---------+ | | | | | + | +----------+ | | streaming streaming | | + | | | +---------v---------+ | + | | | +-----v------+ control | | pulseaudio+dlna +---> upmpdcli | + | | | +-----+------+ | | +-------------------+ | | | streaming +----------+ | | | | | | | +---------> mopidy <------------+ | +------------------+ +---> | | | sources | | +----+-----+ | |(emby/youtube/.

Ich sitze krank zuhause. Was stellt man an? Der Kopf brummt und das so laut und intensiv, dass man am besten, natürlich kontraproduktiv, an seinem Linux-Audio-Setup rumschraubt. Oder ist dies der Grund für mein Unbehagen? Was kann denn da schon schiefgehen? Ich starte mal mit einem kleinen Schaubild meines Setups.

                                                +------------------------+
                                                |                        |
                                                |                        |
+----------------------+                   +----v-----+                  |
|  firefox/mpv/...     |                   |          |                  |
|  through pulseaudio  |         +---------+  client  +------------+     |
+------------+---------+         |         |          |            |     |
             +                   |         +----------+            |     |
         streaming           streaming                             |     |
             +                   |                                 |     |
   +---------v---------+         |                                 +     |
   |                   |   +-----v------+                       control  |
   |  pulseaudio+dlna  +--->  upmpdcli  |                          +     |
   |                   |   +-----+------+                          |     |
   +-------------------+         |                                 |     |
                             streaming     +----------+            |     |
                                 |         |          |            |     |
                                 +--------->  mopidy  <------------+     |
       +------------------+            +--->          |                  |
       |      sources     |            |   +----+-----+                  |
       |(emby/youtube/...)+-streaming--+        |                        |
       +------------------+                +----v-----+                  |
                                           | snapcast |                  |
                                           +----+-----+                  |
                                                +                        |
                  +------------------+      streaming                    |
                  |                  |          +------------------------+
                  |   snapclient on  |          |
                  |   raspberry pi   <----------+
                  |                  |          |
                  +------------------+          |     +--------------+
                                                +-----> more         |
                                                      | snapclients  |
                                                      +--------------+

Im Endeffekt ist die Schaltzentrale mopidy mit seiner Möglichkeit Audio aus mehreren Sourcen entgegen zu nehmen und sie auch auf verschiedenste Möglichkeiten wieder auszugeben. Der Grundgedanke mit mopidy war wohl: ein Server der über das Netzwerk Lieder entgegennimmt und sie dann über, die an dem Server angeschlossenen, Lautsprecher abspielt. Nun sind wir im großen Streaming-Zeitalter und wieso sollte ich für jeden Raum den ich bespielen möchte einen mopidy-Dienst betreiben? Also schiebt mopidy seinen Output in einen snapcast-Server, der dann von vielen Clients angefragt werden kann und das Audio versucht, synchronisiert, abzuspielen. Daran können viele Clients sich ihr Audio abholen. Ich habe einen Raspberry Pi im Wohnzimmer an meine Stereoanlage angeschlossen auf dem ein snapcast-Client läuft. Zusätzlich nutze ich die snapcast-App auf meinem Telefon an dem ich Aktivlautsprecher anschließe und diese in der Küche betreibe. Funktioniert sehr gut. Sogar über VPN nutze ich snapcast. So kann ich per mopidy mir Lieder auswählen, aus egal welchen Quellen, und sie dann einfach abspielen, egal wo ich bin. Eine Komponente ist upmpdcli. Damit läßt sich mopidy durch MPD-Clients steuern. Der Clou: Man kann sogar per UPNP Tracks durch upmpdcli zu mopidy durchreichen. Dies funktioniert nicht immer perfekt aber immer wieder überraschend gut. Nun fing ich an zu googeln. Es wäre natürlich am schönsten, wenn ich so auch Inhalte meines Laptops in mein Audio-Setup geben könnte. Und natürlich gab es da etwas: pulseaudio-dlna! Und wer hätte das denken können: ich bin mittlerweile voll auf dem Pulseaudio-Zug, begeistert, aufgesprungen. Hat ja nur wie lange gedauert? Zehn Jahre? Damals wurde es zu dem Standard in Ubuntu und alles was es konnte war, dass es keinen Sound mehr gab. Zumindestens auf meinem System. Nun nutze ich es gerne für genau solche Setups. Man started pulseaudio-dlna und öffnet pavucontrol. Spielt nun ein Programm Audio ab, kann man es über einen UPNP-Renderer (in meinem Fall upmpdcli + mopidy) ausgeben lassen oder sogar über einen Chromecast.

Übrigens versuche ich die meiste Software für Alpine Linux zu Verfügung zu stellen, wenn es sie nicht schon gibt. Ich versuche all meine Docker-Images auf Alpine basieren zu lassen. So kommt alles aus einem Guss und ich kann auch noch ein wenig diese tolle Distro unterstützen.

Mein ganzes Setup ist in Docker gegossen. Alles dazu findet man hier.



Docker aufs auf einem Ubuntu 16.04 LTS

/// d794cd8 /// ubuntu docker linux mastodon

Ich bin gestern auf was gestossen. Ich betreibe einen kleinen Server mit einer Mastodon Instanz mit nur mir als User. Der Server auf dem dies läuft ist ein Ubuntu 16.04 LTS. Das neue Mastodon Docker Image hat einen Entrypoint der bei jedem Aufruf des Containers erstmal einen Haufen Files nach ihren Benutzerrechten abfragt und diese dann gegebenenfalls ändert. Dies hat beim letzten Update dann einfach Stunden gedauert, da bei jeder Datenbank-Migration oder Assert-Kompilierung erstmal wieder alle Files durchkämmt wurden.

Ich bin gestern auf was gestossen. Ich betreibe einen kleinen Server mit einer Mastodon Instanz mit nur mir als User. Der Server auf dem dies läuft ist ein Ubuntu 16.04 LTS.

Das neue Mastodon Docker Image hat einen Entrypoint der bei jedem Aufruf des Containers erstmal einen Haufen Files nach ihren Benutzerrechten abfragt und diese dann gegebenenfalls ändert. Dies hat beim letzten Update dann einfach Stunden gedauert, da bei jeder Datenbank-Migration oder Assert-Kompilierung erstmal wieder alle Files durchkämmt wurden. Ein Graus. Ein wenig in den Bugreports von Mastodon geschaut und auch was gefunden. Anscheinend liegt das an dem Storagedriver overlay2 und mit aufs sollte dies “viel schneller” gehen. Unter Ubuntu Zesty ist aufs wohl auch der Default. Nun unter 16.04 sieht man davon nichts. Dies kann man mit grep aufs /proc/filesystems überprüfen. Es gibt aber einen Weg…

  1. Mit sudo apt-get install linux-image-extra-$(uname -r) linux-image-extra-virtual die passenden Pakete installieren

  2. Mit sudo modprobe aufs das Kernelmodul laden

  3. grep aufs /proc/filesystems sollte nun was auswerfen

  4. Das File /etc/docker/daemon.conf so anpassen das auch wirklich aufs genutzt wird

     {
         "storage-driver": "aufs"
     }
    
  5. Docker neu starten mit service docker restart

  6. Mit docker info überprüfen ob wirklich aufs genutzt wird

  7. aufs zu /etc/modules hinzufügen damit es auch nach einem Neustart funktioniert

  8. Nun kann /var/lib/docker/overlay2 gelöscht werden. Alle Images müssen nun neu heruntergeladen oder gebaut werden



Audio überall. Mopidy und Snapcast

/// d794cd8 /// mopidy linux raspberrypi python

Musik hören geht alle Zeiten wieder durch mehrere Iterationen. Von wild, ungetaggten, Files die ich bin XMMS abspielte, über die ersten Versuche mit einer Verwaltung unter Amarok und eine sehr düstere Zeit mit, dem vom Herzen tiefst gehassten, iTunes. Das erste Licht am Ende des Tunnels sah ich mit MPD und dem Feature von ncmpcpp Tags und Files umzubenennen und richtig zu taggen. Später dann mit Picard vom MusicBrainz Projekt.

Musik hören geht alle Zeiten wieder durch mehrere Iterationen. Von wild, ungetaggten, Files die ich bin XMMS abspielte, über die ersten Versuche mit einer Verwaltung unter Amarok und eine sehr düstere Zeit mit, dem vom Herzen tiefst gehassten, iTunes. Das erste Licht am Ende des Tunnels sah ich mit MPD und dem Feature von ncmpcpp Tags und Files umzubenennen und richtig zu taggen. Später dann mit Picard vom MusicBrainz Projekt. Dann kam ich schnell auf ein Kommandozeilen-Tool was dies auch ganz wunderbar kann: beets. Ab da wollte ich mir nicht mehr von anderen Programmen in meine Files schreiben lassen und erst recht nicht sortieren. Nun gibt es Clouds oder ähnliche Marketingkonstrukte und man installiert sich Software auf dem Heimserver wie Plex oder seine Open-Source-Alternative Emby. Mit diesen Diensten kann man seine Mediensammlung theoretisch über das Internet konsumieren. Klappt in den besten Fällen ziemlich gut. Gerade die UI von Emby kommt mir ziemlich behäbig vor (kann natürlich auch an meinem Server liegen). Ich erinnerte mich an meine unbeschwerte Zeit mit MPD. Alles flutschte und funktionierte mit den verschiedensten Clients. Nun gibt es ein kleines Projekt mit dem Namen Mopidy. Ein in Python geschriebener Audio Server der so tut als ob er MPD ist aber noch viel mehr zu bieten hat. Es gibt Plugins um auf die verschiedensten Bibliotheken zuzugreifen. Unter anderem Spotify, Youtube, Google Music, usw. Also habe ich mich mal ran gesetzt und ein Emby Plugin geschrieben. Nun kann ich das langsame UI umgehen und viele weitere Möglichkeiten machen sich auf.

Ich lasse Mopidy und Snapcast in einem Docker Container laufen. Snapcast bietet Multi-Room Streaming. Mehrere Devices die über das Netz ihren Output synchronisieren und das bei mir sogar über ein VPN hinweg. So kann ich Raspberry Pi’s über all im Haus verteilen. Diese starten dann den snapclient nach dem booten und die Musik spielt ab.

Mopidy muss dazu den Output in ein FIFO-File schreiben. Dazu muss in der Config folgendes stehen:

[audio]
output = audioresample ! audioconvert ! audio/x-raw,rate=48000,channels=2,format=S16LE ! wavenc ! filesink location=/tmp/snapfifo

Den Server startet man mit den Defaults mit einem einfachen snapserver. Der Client sollte den Server über Avahi finden. Da ich den Port an meinem Container nicht freigegeben habe mache ich das manuell mit snapclient -h 192.168.1.77. Mein Docker Setup findet man hier.



xonsh aus Bash heraus starten

/// d794cd8 /// xonsh python linux

Noch ein kleiner Nachtrag zu dem Artikel von gestern. Heute wollte ich mal wieder in meiner Blogging Vagrant Docker Box alles auf den neusten stand bringen. Ich benutze dazu vagrant provision. Dies startet das Provisioning, also holt meine neuen Config-Files. installiert neue Software usw. Ansible benutzt SSH um auf die Vagrant Box zuzugreifen und dies war auf einmal ein Problem. Ansible lief sofort an die Wand. /bin/sh: sudo -H -S -n -u root /bin/sh -c 'echo BECOME-SUCCESS-mtobhjchhszgqaaixzbsbsolwbuprmhn; LANG=de_DE.

Noch ein kleiner Nachtrag zu dem Artikel von gestern. Heute wollte ich mal wieder in meiner Blogging Vagrant Docker Box alles auf den neusten stand bringen. Ich benutze dazu vagrant provision. Dies startet das Provisioning, also holt meine neuen Config-Files. installiert neue Software usw. Ansible benutzt SSH um auf die Vagrant Box zuzugreifen und dies war auf einmal ein Problem. Ansible lief sofort an die Wand.

/bin/sh: sudo -H -S -n -u root /bin/sh -c 'echo BECOME-SUCCESS-mtobhjchhszgqaaixzbsbsolwbuprmhn; LANG=de_DE.UTF-7 LC_ALL=de_DE.UTF-8 LC_MESSAGES=de_DE.UTF-8 /usr/bin/python /home/vagrant/.ansible/tmp/ansible-tmp-1469778394.25-104576843898387/apk; rm -rf \"/home/vagrant/.ansible/tmp/ansible-tmp-1469778394.25-104576843898387/\" > /dev/null 2>&1' && sleep 0: not found\r\n

Loggte ich mich ein und führe das Ansible Playbook manuell aus, kein Problem. Ich hatte xonsh als Login Shell eingerichtet und da läuft etwas schief. SSH ruft den Befehl per exec auf und da hat xonsh noch ein paar Probleme. Es gibt den Shell Befehl exec und die Python-Funktion exec. Diese sind schwierig zu unterscheiden und schon läuft es schief. Es wird an einem Fix gearbeitet. Der soll auch in den nächsten Tagen released werden. Bis dahin habe ich mich dazu entschieden meine Login-Shell wieder auf Bash umzustellen und daraus dann xonsh zu starten.

Dazu benutze ich das File ~/.profile. Dies ist dafür da Sachen beim Login auszuführen. Aber nur wenn es ~/.bash_profile und ~/bash_login nicht gibt. Ich erweitere also ~/.profile.

[ -f /usr/local/bin/xonsh ] && exec /usr/local/bin/xonsh

Wenn es /usr/local/bin/xonsh gibt dann führe es aus.

Ich nahm an das es so funktionieren würde. Falsch gedacht. Es gab einige Probleme. Das schwerwiegendste war das LightDM mich nicht mehr einloggen wollte. Es liest beim einloggen die ~/.profile und der exec Befehl behindert das ausführen von i3. Das starten von xonsh aus .profile heraus erschien mir als ein guter Weg. Aber sind wir mal ehrlich: Das ich xonsh nicht per chsh setzen kann, endet wohl oder übel in heftigstes, unsauberes gefrickel. Was macht man sonst an einem Samstag Vormittag? Nun kam ich zu folgender Lösung: Ich starte xonsh aus der ~/.bashrc mit:

[ -f /usr/local/bin/xonsh ] && exec /usr/local/bin/xonsh

Überraschung: die wird nicht immer geladen. SSH auf eine Alpine Linux Box warf mich in eine bash Shell. Also sollte man sich eine ~/.bash_profile anlegen mit folgenden Inhalt:

if [ -f ~/.bashrc ]; then
  . ~/.bashrc
fi

Nun geht erstmal wieder alles. Ein Workaround… aber was solls? Ich warte auf das nächste Krachen. Aber am meisten freue ich mich auf den Fix.



Meine neue Shell: xonsh

/// d794cd8 /// python xonsh linux

Manchmal macht man Sachen die man selber nicht so wirklich versteht. Zum Beispiel hatte ich mich lange quer gestellt ZSH einzusetzen. Wieso eine alternative Shell? BASH ist doch sowas wie ein standard auf den Servern auf denen ich arbeite. Vor ein paar Jahren dann die erste Installation. Total abgeschreckt von der Konfiguration (bin bis Heute nicht dahinter gekommen wie das alles funktioniert), war mir oh-my-zsh ein Steigbügelhalter in die Welt von ZSH.

Manchmal macht man Sachen die man selber nicht so wirklich versteht. Zum Beispiel hatte ich mich lange quer gestellt ZSH einzusetzen. Wieso eine alternative Shell? BASH ist doch sowas wie ein standard auf den Servern auf denen ich arbeite. Vor ein paar Jahren dann die erste Installation. Total abgeschreckt von der Konfiguration (bin bis Heute nicht dahinter gekommen wie das alles funktioniert), war mir oh-my-zsh ein Steigbügelhalter in die Welt von ZSH. Es ist eine Paket aus verschiedenen Plugins und Konfigurationen die das erste ZSH-Erlebnis, instant, beeindruckend gestaltet. Irgendwann war es mir zu langsam und ich stieg um auf prezto. Hat sich alles ein wenig flotter angefühlt und ich habe noch weniger verstanden was da im Hintergrund abläuft. Ja, man sollte sich die Sachen genauer anschauen und dann wäre es auch kein Problem. Aber manchmal fehlt mir Zeit weil ich meine Nase in tausend anderen Projekten habe. Es soll einfach funktionieren und the world a better place machen tun… oder zumindest meinen Alltag.

Nun bin ich auf einen Beitrag auf der PyCon über xonsh gestossen.

Es ist eine alternative Shell die in Python geschrieben ist und das Bindeglied zwischen Shell und Python sein möchte. Und das beste: Endlich eine Syntax in der Shell die ich mir merken kann. Nie wieder nachschauen wie if oder for in Bash funktionieren. “Nie wieder” ist ein harter Ausdruck. Selbst ich installiere xonsh nicht auf all meinen Servern. Es setzt Python 3.4 oder 3.5 voraus.

Bevor ich ein paar Anwendungsbeispiele nenne, möchte ich auf mein Setup hinweisen. Auf GitHub befinden sich meine Ansible Roles um meine Rechner einzurichten. Unter anderen auch xonsh.

Python und Shell Kommandos gemeinsam nutzen

Hier ein Beispiel für ein xonsh-Script das den Output von allen Docker-Commands nimmt und den Output in Files schreibt. Dies habe ich gebraucht um meine xonsh Extension xonsh-docker-tabcomplete zu testen. Ein Completer für Docker Kommandos die auch direkt auf die Docker-API zugreift. Danke docker-py und Python als Shell.

    """Ein xonsh-Script um alle `--help` Outputs als Textfiles
    abzuspeichern.
    """
    import os
    import re

    # `parser` ist ein Modul mit ein paar Regex Parsern.
    from docker_tabcomplete import parser

    # Regex um Versionsnummer zu finden.
    RE_VERSION = re.compile(r'((?:\d+\.)?(?:\d+\.)?(?:\*|\d+))')

    # Hier nutze ich das erste mal ein wenig xonsh Magic.
    # Das `$()` macht genau das gleiche wie in Bash-Scripten. Es führt
    # Ein Kommando aus aus gibt den Output zurück. In diesem Fall den
    # Output von `docker --version` und sucht in Python per Regex nach
    # der Versionsnummer.
    DOCKER_VERSION = re.search(RE_VERSION, $(docker --version)).group(1)

    # Es wird ein Verzeichnis für die Docker Version angelegt falls es noch
    # nicht existiert.
    if not os.path.exists('../tests/data/{}'.format(DOCKER_VERSION)):
        os.makedirs('../tests/data/{}'.format(DOCKER_VERSION))

    # `parser.commands` nimmt den Output von `docker --help` und parst
    # nach allen Docker Kommandos. Wieder muss nicht mit `subprocess`
    # gefummelt werden sondern wir nutzen einfach `$()` von xonsh um
    # an den Output zu gelangen.
    COMMANDS = parser.commands($(docker --help))

    # Jetzt wird sogar Python mit Shell mit Python gemischt.
    for command in COMMANDS:
        with open('../tests/data/{}/{}.stdout'.format(DOCKER_VERSION, command), 'w') as f:

            # Es wird die Python-Variabel `command`, die gerade in benutzung ist,
            # in das Shell-Kommando `docker ... --help` eingefügt. Magic.
            f.write($(docker @(command) --help))

Dieses Script speichert man als create_test_data.xsh und kann es mit xonsh create_test_data.xsh ausführen.

Meine ~/.xonshrc

Yeah, endlich eine Shell Konfiguration in Python…

import os
import shutil

from xonsh.environ import git_dirty_working_directory


# XONSH ENVIRONMENT VARIABLES ############

# Die Environment Variabeln werden wie Python Variabeln definiert.
# Hier einige Beispiele. Diese sind vor allem um das Verhalten von
# xonsh anzupassen.

# xonsh supportet die bestehenden bash completion files.
# Zumindestens die meisten. Ich hatte Probleme mit dem
# Docker-Completion File und habe deswegen mein eigenes geschrieben.
$BASH_COMPLETIONS = ['/etc/bash_completion.d', '/usr/share/bash-completion/completions']

$CASE_SENSITIVE_COMPLETIONS = False
$SHELL_TYPE = 'best'
$SUPPRESS_BRANCH_TIMEOUT_MESSAGE = True
$VC_BRANCH_TIMEOUT = 5
$XONSH_COLOR_STYLE = 'monokai'

# OTHER ENVIRONMENT VARIABLES ############
$EDITOR = 'vim'
$PYTHONIOENCODING = 'UTF-8'
$LC_ALL = 'C.UTF-8'
$SSL_CERT_FILE = '/etc/ssl/certs/ca-certificates.crt'

# ALIASES ################################

# Aliase sind in einem dict gespeichert das einfach erweitert werden kann.

aliases['exec'] = aliases['xexec']
aliases['ll'] = 'ls -la'
aliases['tmux'] = 'tmux -2'

# Jetzt wird es spannend: Wir können Funktionen oder Lambdas als
# Aliase definieren. Hier der Fall das Mosh eine bestimmte
# Environment Variabel gesetzt haben muss.
def _alias_mosh(args, stdin=None):
    """A function to use as alias to get mosh running.

    There is a strange problem with mosh and xonsh. I have to set $SHELL to
    /bin/bash before running it. It should work with this little hack.
    """
    os.environ['SHELL'] = '/bin/bash'
    args.insert(0, 'mosh')
    cmd = ' '.join(args)
    os.system(cmd)

aliases['mosh'] = _alias_mosh

# GIT ####################################
$FORMATTER_DICT['branch_color'] = lambda: ('{BOLD_INTENSE_RED}'
                                           if git_dirty_working_directory(include_untracked=True)
                                           else '{BOLD_INTENSE_GREEN}')

# PATH ###################################

# Die Path Variabel ist eine Liste die ich einfach extende.
$PATH.extend(
    [
        $HOME + '/.local/bin',
        $HOME + '/miniconda3/bin',
        $HOME + '/node_modules_global/bin'
    ]
)

# XONTRIB ################################

# Hier habe ich ein paar Erweiterungen enabled. Bei manchen schaue ich
# ob der Befehl auf denen sie beruhen überhaupt da und einsatzfähig ist
# bevor ich sie anschalte. Was nützt mir `apt-get` wenn ich nicht auf
# einem Debian/Ubuntu System bin oder Docker nicht installiert ist?
# Dies teste ich mit `shutil.which`.
xontrib autoxsh vox_tabcomplete

if shutil.which('apt-get'):
    xontrib apt_tabcomplete

if shutil.which('docker'):
    xontrib docker_tabcomplete

Ein wenig Rescue

Natürlich kann alles noch ein wenig buggy sein. Einmal gab es Probleme mit den Schreibrechten auf dem History-File und ich konnte kein Terminal mehr öffnen oder mich einloggen. Was da helfen kann: Per SSH und anderer Shell draufschalten und die Shell ändern.

ssh meinuser@meinrechner sh

Fazit

Das ist natürlich nur ein Bruchteil der Möglichkeiten und Sachen die man ausprobieren kann. Es ist ein Anfang und bis jetzt bin ich begeistert. Also einfach mal das Youtube Video anschauen und ausprobieren. Muss ja nicht gleich die Standard-Shell werden wie bei mir ;-).



Ansible, win_package und Upgrades

/// d794cd8 /// ansible windows linux

Das Ansible Modul win_package beruht auf die Annahme das die product_id in der Registry vorhanden ist oder eben nicht. Davon macht es abhängig ob ein Paket installiert werden soll oder ob es schon vorhanden ist. Nun kann es ja auch vorkommen das man ein Paket, obwohl es laut Registry schon installiert ist, es noch einmal installieren möchte. Quasi ein Upgrade machen. Schön wäre es wenn er nicht nur schaut ob das Paket installiert ist, sondern auch die installierte Version.

https://childofmoonlight.tumblr.com/post/44755113585/im-a-grumpy-guss-enjoy-this-gif-set-of-grumpy

Das Ansible Modul win_package beruht auf die Annahme das die product_id in der Registry vorhanden ist oder eben nicht. Davon macht es abhängig ob ein Paket installiert werden soll oder ob es schon vorhanden ist. Nun kann es ja auch vorkommen das man ein Paket, obwohl es laut Registry schon installiert ist, es noch einmal installieren möchte. Quasi ein Upgrade machen. Schön wäre es wenn er nicht nur schaut ob das Paket installiert ist, sondern auch die installierte Version. Daran könnte man Task Entscheidungen treffen. Dies mache ich nun manuell. Ein Beispiel für VLC:

---

# Der ganz normale Install-Task. Es wird nach der product_id gesucht gegebenenfalls installiert
- name: install
  win_package:
    product_id="VLC media player"
    path="//myserver/updates/software/vlc/vlc-2.2.2-win32.exe"
    arguments="/L=1031 /S"

# Ein Powershell Snippet das die Version des installierten Pakets in der Variabel "version" speichert
- name: check version
  raw: (Get-ItemProperty "HKLM:\SOFTWARE\wow6432node\Microsoft\Windows\CurrentVersion\Uninstall\VLC media player").DisplayVersion
  register: version

- name: upgrade
  win_package:
    product_id="VLC media player upgrade" # Muss anders sein damit das Paket nochmal installiert wird
    path="//myserver/updates/software/vlc/vlc-2.2.2-win32.exe"
    arguments="/L=1031 /S"
  register: upgrade_results # Speichert das Ergebnis des Tasks in die Variabel "upgrade_results"
  changed_when: '"was installed" in upgrade_results.msg' # Ändert den Status auf "changed" wenn der String "was installed" im Ergebnis ist
  failed_when: '"was installed" not in upgrade_results.msg' # Status "failed" wenn "was installed" nicht im Ergebnis ist
  ignore_errors: True # Sonst bricht er ab bevor der Task überhaupt die Variabel "upgrade_results" befüllt
  when: '"2.2.2" not in version.stdout_lines' # Den Task nur ausführen wenn die Version eine andere ist


1 of 3