Zum Hauptinhalt springen
Chris’ wirre Gedankenwelt

Another example for bad testing

Today I discovered this in salt.

Someone tries to test the function iptables.get_saved_rules in salt/tests/unit/modules/iptables_test.py#L149

@patch('salt.modules.iptables._parse_conf', MagicMock(return_value=False))
def test_get_saved_rules(self):
    '''
    Test if it return a data structure of the rules in the conf file
    '''
    self.assertFalse(iptables.get_saved_rules())

But in fact acutally nothing is tested at all. Literally nothing.

So lets have a look at the corresponding code. salt/salt/modules/iptables.py#L471

def get_saved_rules(conf_file=None, family='ipv4'):
    '''
    Return a data structure of the rules in the conf file
    CLI Example:
    .. code-block:: bash
        salt '*' iptables.get_saved_rules
        IPv6:
        salt '*' iptables.get_saved_rules family=ipv6
    '''
    return _parse_conf(conf_file, family)

This function just forwards some parameters to another function _parse_conf. The above test mocks out exactly this call, but does not check anything about the mock. This test does not even cover deleting the only statment in this function! This is caused to self.assertFalse, which is not very strict and accepts None as False.

However, I discovered this code, since ipv6 is not working correctly, due to wrong parameter passing to _parse_conf. Having a look there, shows us the use of keywords-only.

def _parse_conf(conf_file=None, in_mem=False, family='ipv4'):
    '''
    If a file is not passed in, and the correct one for this OS is not
    detected, return False
    '''
    ...

So in case of get_saved_rules, ipv4 is passed as parameter in_mem instead of family. So the code always runs the ipv4-path.

Long story short. If you are mocking. Check if your expected call really happened. This code really tests the call for _parse_conf, with expected parameters:

def test_get_saved_rules(self):
    '''
    Test if it return a data structure of the rules in the conf file
    '''
    mock = MagicMock(return_value=False)
    with patch.object(iptables, '_parse_conf', mock):
        self.assertFalse(iptables.get_saved_rules())
        mock.assert_called_with(conf_file=None, family='ipv4')

Even this is not best practice, since calling stuff on mock objects, also silently irgnores errors. e.g. mock.fajdslksahgalds() would work totally fine, without any info. Most important in such cases. Write your test before the code, so you see the test failing once.

… Yes. I opened pull requests for both issues.

Autor
Chris Glaubitz
Configuring, coding, debugging computers for a living. Riding bikes for fun.

Wie hilft $orchestrationtool bei einer Datenmigration

TL;DR: Vollautomatisierung hilft die Migration immer wieder zu testen

In der letzten Zeit habe ich diverse Workshops zum Thema Orchestration gegeben. Zusammen mit meinem geschätzten Kollegn Jan haben wir Gruppen von 3 bis max. 10 Leuten den umgang mit OpenStack Orchestration (Heat) nahe gebracht. Dabei haben wir uns ausschließlich auf Heat Orchestration Templates (HOT) konzentriert. Unsere Teilnehmer waren in einigen Fällen Admins oder DevOps (sie wurden als solche vorgestellt) und weitaus häufiger Software Engineers/Architects.

So viel erst einmal zur Situation. Heat verwende ich hier nur als Synonym für jedes x-beliebige Tool, mit dem eine verteilte Anwendung aufgesetzt werden kann. Ein weiteres Beispiel, dieses mal für Container, wäre Googles Kubernetes.

Was haben wir den Teilnehmern beigebracht? #

Nachdem wir die einzelnen OpenStack-Komponenten gezeigt haben, die sich mittels Heat steuern lassen, sind wir direkt zu Heat selber und HOT gegangen. Als einfaches Szenario hat immer eine Applikation mit mehreren Application-Servern, einem davor sitzenden Loadbalancer und einem Datenbank-Server gedient. Auf primitivste Weise haben wir ein Service-Discovery gebaut, so dass neue Application-Server ins LoadBalancing aufgenommen werden. Das zuletzt verwendete Beispiel findet sich im Github Repo example-projects.

Wir haben gezeigt wie mittels HOT eine Infrastruktur inklusive Netzwerk und Firewall ausgerollt werden kann. Innerhalb der VMs wird (wie auch bei AWS) cloud-init angestoßen. Mittels diesem werden die nötigen Anwendungen installiert, konfiguriert und gestartet. Mit diesen kleinen Automatisierungen ist es auch möglich beliebig viele Application-Server nachträglich einzuzufügen. Natürlich gehört in der Realität etwas mehr dazu VMs komplett zu provisionieren und eine Applikation zu deployen. Aber als Proof-of-Concept ist das immer ganz gut angekommen.

Wir wollen den Teilnehmern mehr mitgeben #

Zum einen zeigen wir, wie einfach es sein kann, eine komplett neue Infrastruktur mit allen Komponenten hoch zu ziehen. Viel wichtiger aber ist die sensibilisierung auf Cloud-Best-Practices. Siehe dazu The twelve-factor app, oder mein persönliches Fazit zu docker.

Für ein Projekt ergeben sich durch die vollständige Automatisierung gleich mehrere Vorteile. So können Entwickler und QA die Applikation quasi im Production-Environment testen. Dadurch wird die Automatisierung auch immer und immer wieder getestet. Man hat also immer Übung für den Ernstfall: Alles muss von Grund auf neu aufgebaut werden.

Das starten und provisionieren von neuen Instanzen wird durch die Automatisierung einfacher. Das bedeutet, dass kein dedizierter Personenkreis benötigt wird um die Application-Server hoch zu skalieren. Womit ich auf keinen Fall Admins als überflüssig erachte! Die können sich aber auf wichtige Dinge konzentrieren, statt Arbeit zu erledigen, die man auch einem Äffchen trainieren könnte. Nehmen wir einfach folgendes an. Der einzige Admin, der die Infrastruktur aufsetzen kann, ist vor dem Black Friday krank. Es müssen aber unbedingt noch mehr Application-Server. In unserem Szenario kein Problem. Im Endeffekt kann jeder(tm) einen Heat-Stack mit Application-Servern daneben stellen. Sicherlich gilt hier Faktoren wie die maximale Anzahl an Verbindungen auf dem DBMS zu beachten. Aber das führt hier zu weit.

Wie auch für das Beispiel des Black Fridays, kann ein neues Release der Applikation durch einen zusätzlichen Heat-Stack ausgerollt werden. Dabei werden einfach neue Instanzen mit der neuen Version gestartet und die alten einfach abgeschaltet.

Genau an dieser Stelle kommt häufig die Frage: Wie hilft uns Heat bei einer Datenmigration?

Wie hilft uns Heat bei einer Datenmigration? #

(Wobei als Datenmigration auf Nachfrage immer eine Änderung des SQL-Schemas war.)

Also… Heat bzw. jedes andere Orchestrationtool hilft in erster Linie genau gar nicht. Zu erst einmal sollte man wissen, dass ein ALTER TABLE in den meisten DBMS nicht um sonst ist (siehe z.B. Avoiding MySQL ALTER table downtime, Schema changes – what’s new in MySQL 5.6?). Gerade wenn eine Tabelle groß wird, sollte man wirklich einen geeigneten Migrations-Plan haben!

Auf jeden Fall gehört eine Migration nicht in den Code (schon gar nicht in den Startup-Code) eines neuen Releases! Keiner möchte eine Situation haben, in der ein neues Release installiert wird und gleich die laufende Production-Umgebung mit den Abgrund zieht (z.B. durch einen expliziten Lock durch ein ALTER TABLE). Die einfachste Möglichkeit Schema-Änderungen zu verhindern ist wohl IKEA Kloppe. Einfach mal damit bei dem entsprechenden Entwickler vorbeischauen.

Neben den möglichen Problemen bei einem ALTER TABLE, kann die vorherige Version der Applikation (App-1.0) mit dem neuen Schema nichts anfangen und daher in Fehler laufen. Im besten Falle hält man die Schema-Änderungen so klein, dass genau dies nicht passiert. Ein zusätzliches Feld wird einem ORM z.B. noch nicht weh tun. Ein Feld entfernen wird man dagegen erst wirklich wenn es nur noch Applikationen gibt, die ohne dieses Feld auskommen.

In jedem Fall sollte mit versionierten DB-Schemata gearbeitet werden, so dass App-2.0 auch mit dem Schema von App-1.0 arbeiten kann, dafür aber ggf. Features noch nicht freigeschaltet sind.

Bleibt die zu vermeidende Situation: Man benötigt ganz ganz ganhanz dringend ein anderes Schema und bricht dadurch App-1.0. Hier gilt es App-2.0 erst einmal überall auszurollen. Ggf. zuerst einmal mit reduziertem Feature-Set. Natürlich wird App-2.0 ohne Migration der DB an genau der selben Stelle fehlschlagen wie auch App-1.0. Doch lässt sich hier gezielt das neue Schema probieren, die Exception fangen und auf das alte Schema zurückfallen.

Vollautomatisierung hilft #

Natürlich stimmt meine Aussage von oben nicht. Orchestration und Vollautomatisierung hilft sehr wohl. Zwar gehört die Logik für dem Umgang mit Migrationen in die Applikation, also in den Kompetenzbereich der Entwickler, doch nicht die Migration selbst. Die Vollautomatisierung hilft einfach und schnell ein komplettes Setup aufzubauen und die Migration immer und immer wieder zu testen, so lange bis das Zero-Downtime-Deployment funktioniert. Ist die Änderung doch zu groß um diese ohne Downtime durchzuführen, kann wenigstens der zu erwartende Zeitraum abgeschätzt werden und entsprechende Maßnahmen treffen.

Autor
Chris Glaubitz
Configuring, coding, debugging computers for a living. Riding bikes for fun.

Monkey patch a puppet provider

I just wanted to monkey patch the puppet package provider for gentoo portage.

But why?

# puppet resource package

Only shows packages installed by puppet, but not packages installed by hand. The apt/dpkg- and yum/rpm-provider list all packages.

It was not enough to place a lib/puppet/provider/package/portage.rb. In all my tests, the original behavior was kept. I only want to replace one function.

But how?

When registering new type or providers, etc, puppet is doing a bunch of magick in the background. So first, I have to figure out the name of the automagically created class.

https://github.com/puppetlabs/puppet/blob/master/lib/puppet/provider/package/portage.rb#L4

Puppet::Type.type(:package).provide :portage, :parent=> Puppet::Provider::Packagedo
  ...
end

Trying this in irb

irb(main):001:0> require 'puppet'

irb(main):002:0> Puppet::Type.type(:package).provide :SOMETEST
=> Puppet::Type::Package::ProviderSometest

Hmm. Is it that simple? In one of our modules, I generated a file lib/puppet/provider/package/portage_zz.rb

require 'puppet'


class Puppet::Type::Package::ProviderPortage
    private
    def self.eix_search_format
      "'<category> <name> [<installedversions:LASTVERSION>] [<bestversion:LASTVERSION>] <homepage> <description>\n'"
    end
end

I called it portage_zz.rb, to be sure it will be loaded after the regular portage.rb.

However, the puppet run with pluginsync shipped this file. And... puppet resource package list all packages :)

https://github.com/puppetlabs/puppet/pull/3765

Autor
Chris Glaubitz
Configuring, coding, debugging computers for a living. Riding bikes for fun.

redis subscribe to key

Redis has a buildin pubsub mechanism, but by default it is not possible to listen on specific keys. Redis 2.8 intruduced Keyspace Notifications , to which you can subscribe.

First of all. This feature is disabled by default, so we need to enable it

$ redis-cli config set notify-keyspace-events KAE

This little python-script listens on keyspace-Events containing stash:silence/*

#!/usr/bin/env python

import redis

r = redis.StrictRedis()

pubsub = r.pubsub()
pubsub.psubscribe('__keyspace@*:stash:silence/*')
for msg in pubsub.listen():
    print(msg)

Results to:

{'pattern': '__keyspace@*:stash:silence/*', 'type': 'pmessage', 'channel': '__keyspace@0__:stash:silence/controller.local/check_instance_snat', 'data': 'expired'}

{'pattern': '__keyspace@*:stash:silence/*', 'type': 'pmessage', 'channel': '__keyspace@0__:stash:silence/compute.local', 'data': 'set'}
{'pattern': '__keyspace@*:stash:silence/*', 'type': 'pmessage', 'channel': '__keyspace@0__:stash:silence/compute.local', 'data': 'expire'}

Why?

We are using sensu for monitoring. If someone press the stash-Button (or creates a stash via the sensu-api) we want to send out an ACK email. The sensu-api only inserts into redis and does not inform anyone about the stash. It would be possible to poll the api peridically, but man... it's 2015.

Autor
Chris Glaubitz
Configuring, coding, debugging computers for a living. Riding bikes for fun.

StackStorm


erstes fazit:: | Sehr "pluggable". Lässt sich mit so gut wie jedem System zusammen stecken. StackStorm hat so viele Anwendungsfälle wie man sich vorstellen kann. Darf nur in einem Vertrauten Netz stehen. Zumindest habe ich keine auth-Möglichkeit gesehen. Fühlt sich aber noch hakelig an. tl;dr: stackstorm ist ein automatisierungsframework. es gibt eine zentrale stelle an der events einlaufen und aktionen veranlasst werden. es ist relativ einfach eigene automatisierung darin zu realisieren.: | Siehe: http://stackstorm.com/product/ #

 

Zuerst einmal. Worum geht es eigentlich?

Auf der Suche nach eine Orchestrierungslösung bin ich über StackStorm http://stackstorm.com/ gestolpert, die auf den ersten Blick recht vielversprechend aussieht.

Doch einen Schritt zurück. Was meine ich hier mit "Orchestrierung"?

Dinge sollen im besten Fall automatisch auf Computern passieren. Viele Szenarien lassen z.B. sich mittels puppet und mcollective lösen, doch oft stößt man an Grenzen. In solchen Fällen werden häufig Scripte geschrieben, die auf dem kurzen Weg über das Configuation Management ausgerollt werden. Je nach Funktion wird noch ein cronjob ergänzt. Dadurch wächst relativ schnell ein Wildwuchs an Scripten an und man verliert schnell den Überblick wann und wo, was passiert. Im schlimmsten Fall rufen sich Scripte noch gegenseitig über ssh auf.

Oft lassen sich diese Arbeitsanweisungen auf sowas wie "Hier ist X passiert, mache dort Y".

Bsp.: Nach einem git-push wird Jenkins getriggert. Jenkins läuft durch und triggert im Erfolgsfall das Ausrollen auf entsprechenden Nodes.

Technisch ist dies sicherlich lösbar. Hie und da ein Script, vielleicht noch irgendwo ein cronjob, und fertig ist die Laube. Der Nächste wundert sich dann wieder ob der Magie und muss die Kette versuchen zu debuggen. Bei Umzügen brechen dann Ketten, an die niemand mehr gedacht hat.

Ich stelle mir einen zentralen Punkt (bei dem es möglich sein muss, ihn redundant auszulegen) vor, in dem Arbeitsanweisungen definiert werden. Und genau an diesem Punkt bin ich auf StackStorm gestoßen.

StackStorm liefert u.A. sensor, trigger, action und rule. In jede Komponente kann man eigene hinzufügen.

StackStorm selbst besteht aus mehreren Services, die sich je um spezielle Aufgaben kümmern. Die Services kommunizieren über amqp-system wie rabbitmq.

Z.B. führt die action core.remote über ssh (falls gewünscht mit sudo) das übergebene Kommando auf einem entfernten aus. core.http ruft einen entfernten Webhook an. An der Stelle "remote" möchte man vielleicht über eine Queue, statt ssh nachdenken. Die Möglichkeit StackStorm zu erweitern macht das recht einfach.

Sensoren verbinden StackStorm mit externen Systemen und registrieren oft trigger. Webhooks können aber separat als trigger eingerichtet werden.

StackStorm kann Webhooks als trigger zur Verfügung stellen. Angenommen wird jedes beliebige json. Es ist auch möglich cron-Tigger zu definieren.

Rules stecken trigger und action anhand von Kriterien zusammen. Wobei das Kriterium optional ist.

Bsp:

---
    name: "examples.webhook_file"
    description: "Sample rule dumping webhook payload to a file."
    enabled: true

    trigger:
        type: "core.st2.webhook"
        parameters:
            url: "sample"

    criteria:
        trigger.body.name:
            pattern: "st2"
            type: "equals"

    action:
        ref: "core.local" ### passiert auf dem StackStorm-Host
        parameters:
            cmd: "echo \"{{trigger.body}}\" >> /tmp/st2.webhook_sample.out"

Die rule-Engine läuft alle treffenden Regeln durch. So kann man auf ein und den selben trigger verschiedene criteria und actions zusammen stecken.

 

Zusätzlich lassen sich alle actions auch per command line client aufrufen.

st2 run core.local cmd="uname -a"

Jede Auführung wird von StackStorm in der Datenbank gespeichert und ist über

st2 execution list # (/get)

erreichbar. Und genau hier hatte ich in meinen Tests Probleme.

Mein Setup:

Die Installation habe ich nach http://docs.stackstorm.com/install/sources.html durchgeführt und StackStorm via tools/launchdev.sh gestartet. Meine Installation ist noch eine v0.7

sensu mit einem StackStorm-Handler ausgestattet. Dieser hat falls nötig einen Webhook in StackStorm registriert. Von außen, ohne Auth. ACHTUNG: StackStorm nicht im Netz stehen lassen! Außerdem ruft der Handler den Webhook an und lädt das sensu-JSON dort ab.

Der sensu-Hanlder kommt von StackStorm. Kann aber leider nicht mit stashes umgehen. D.h. selbst wenn es in sensu für einen Check/Host einen stash gibt, ruft der Handler trotzdem den webhook an.

Den trigger habe ich mit einer rule ohne criteria mit der action core.local cmd="echo "{{trigger.client.name}}" >> /tmp/st2.sensu.out" zusammen gesteckt.

Bei jedem sensu-Event wurde also der client.name, bei dem das Problem auftrat in eine Datei geschrieben.

---
    name: "sensu.event.process"
    description: "process incoming sensu events"
    enabled: true

    trigger:
        type: "sensu.event_handler"

    action:
        ref: "core.local"
        parameters:
            cmd: "echo \"{{trigger.client.name}}\" >> /tmp/st2.sensu.out"

Ich habe einfach dafür gesorgt, dass ein sensu-Check (der minütlich geprüft wird) rot anzeigt und somit den Webhook anruft. Als ich nach ca. 2 1/2 Wochen wieder an das Thema ging wollte execution list nicht mehr:

# st2 execution list
MESSAGE: database error: too much data for sort() with no index. add an index or specify a smaller limit

Riecht nach "Datenbank + Entwickler = BOOM".

Mit st2 execution get ID kommt man noch an bekannte Ausführungen.

Hier hätte also auch ein core.remote hosts={{trigger.client.name}} cmd="service XY restart" passieren können.

Denkt man sich später irgendwann aus, dass in einem solchen Fall automatisch ein Ticket aufgemacht werden soll. Kein Problem. "Einfach" eine zusätzliche rule einrichten.

StackStorm hat sich als extrem flexibel erwiesen. Der Phantasie sind kaum Grenzen gesetzt. Es tut weitestegehend das was man erwartet und mit Ausnahme von zu viele executions bin ich in keine großen Probleme gelaufen. Bisher habe ich mich noch nicht im reproduzeirbares Aufsetzen und Pakete gekümmert. StackStorm selber kann debian-Pakete bauen. Es gitb ein puppet-Modul (https://github.com/StackStorm/puppet-st2 ) das Pakete per wget runter lädt und installiert. Hier wäre ein repo sicherlich schöner.

Der Grund warum ich initial mit dem dev-Setup angefangen habe: Es ist einfach besser zu debuggen.

Autor
Chris Glaubitz
Configuring, coding, debugging computers for a living. Riding bikes for fun.

Wie baut man ein Desktopsystem für Anwender?

Das ist eine ernst gemeinte Frage. Unter Anwender verstehe ich jemanden wie meine Schwiegermutter, meinen Opa, oder den Arzthelfer.

Ich will nicht unbedingt sagen dass es einen Renter-Modus geben soll. Mit dem Problem von nicht mehr funktionierender oder verstellter Software wird man quasi ständig konfrontiert. Man denke nur an die Anmeldung beim Arzt, oder beim Amt. Ständig? Nein. Das ist übertrieben. Nur dann wenn der Anwender auch Einstellungen verändern kann. Ich kann mich zumindest nur schwer an einen Supermarktbesuch erinnern, an dem die Kasse gestreikt hat.

Aber zurück zu Desktopsystemen, ganz unabhängig vom Benutzten Betriessystem. Ein Anwender-Anwender benötigt nicht besonders viel. Natürlich wird eine Art von grafischer Oberfläche benötigt. Hier sollte eingesetzt werden, mit dem der konkrete Anwender am besten zurecht kommt und was sich am besten in das Ökosystems des Anwenders eingliedert. Besitzt der Anwender z.B. ein iPhone und ein iPad, finde ich es mehr als überlegenswert zu einem Mac zu greifen.  Benutzt der Anwender noch ein Windows XP kann man quasi alles (auch z.B. Cinnamon, oder Mac OS X) nehmen, da der Sprung eh riesig ist. Hier gilt es nicht die eigenen persönlichen Vorlieben durchzusetzen, sondern die des Anwenders!

Was benötigt der Anwender neben der grafischen Oberfläche?

Browser: Auch hier gilt. Persönliche Vorliebe ade. Wir möchten das für den Anwender am besten passende System zusammen schnüren. Unter Mac OS X wird wohl auf Safari zurückgegriffen, unter Windows auf den Internet Explorer, unter Linux ggf. Firefox. Es ist auch zu berücksichtigen welche Anwendung der Anwender auf seinem alten System verwendet. Läuft dort Firefox auch immer in der neusten Version, sollte dies auf dem neuen System berücksichtigt werden, unabhängig vom Betriebssystem.

Mailprogram: Und das nicht einmal zwingend. Liest der Anwender seine Mails über ein Web-Interface, soll das auf dem neuen System auch so bleiben.

Office-Suite: Eigentlich gilt das selbe wie oben. Am besten das gelernte Programm übernehmen, aber auf keinen Fall in der alten Version.

Foto-Suite, Chat(?): s. o.

Viel mehr fällt mir zum Thema "notwendige" Software auch schon nicht ein. Vielleicht kommt noch ein Dateibrowser hinzu. Hier kann sogar die zu verwendende Software auch gleich das Betriebsystem mit beeinflussen. Dies würde ich aber nur gelten lassen, wenn schon die neuste Version auf dem Altsystem zum Einsatz kommt. Muss man ein Update von 10 Jahren machen, kann man auch gleich auf eine andere Software umsteigen. Im besten Fall ist auf dem Altsystem schon die meiste Software in der neusten Version vorhanden.

Ist die benötigte Software soweit klar umrissen, sucht man das Betriebssystem aus. Am besten mit der betroffenen Person zusammen, obwohl sich das oft wegen akuter Ungeduld auf beiden Seiten als schwierig erweist.

Ist das Betriebssystem ausgewählt und installiert werden benötigte Programme installiert und konfiguriert. Sowohl die eigentliche grafische Oberfläche, als auch die Programme. Jedliche nicht benötigte Software fliegt runter. Es werden Panels, Docks, Icons, etc. erstellt und konfiguriert. Meinetwegen wird noch ein schönes Theme ausgewählt und das Hintergrundbild gesetzt. Es sollte eine Grundeinstellung geben und diese mit der betroffenen Person verfeinert werden.

Ein in der letzten Zeit häufig aufgetauchtes Problem sind Tastaturkürzel. Meiner Meinung nach sollten die Tastaturkürzel so weit es geht entfernt werden. Für diese Art von Anwender ist es nicht nötig ein Kürzel für den Wechsel des Tastatur-Layouts anzubieten. Ganz im Gegenteil. Der Anwender drückt aus Versehen eine Tastenkombination und nachher ist das "ß kaputt". Weg damit!

Die Möglichkeiten einen Drucker einzurichten? Weg. Dies gehört ebenfalls zum initialen Aufsetzen des Systems. Wie leicht ist aus versehen ein Drucker gelöscht. Eigentlich sollte man meiner Meinung nach gleich jedliche Konfigurationsmöglichkeit deaktivieren werden. Dies betrifft nicht nur die eigentliche grafische Oberfläche (oder Destkopmanager,  oder wie man es nennen will) sondern die komplette benutzte Software. Wie oft habe ich mir schon Systeme anschauen müssen, bei denen "nichts gemacht wurde", die Konfiguration aber anders war als voher. Z.B. die Sprache auf Spanisch geändert, oder wie schon erwähnt, einfach das Tastatur-Layout geändert. Auch versehentliche Änderungen von Einstellungen des Browsers sind mir schon untergekommen.

Anwender möchten dass das System heute noch weitgehend so funktioniert wie gestern. Dazu unten mehr.

Ich möchte den Anwender hier nicht entmündigen. Wenn er oder sie mit dem System gut zurecht kommt, spricht nichts dagegen die Einstellungen wieder frei zu schalten. Doch annähernd 90% der Familien-Support-Anfragen die ich so bekomme beziehen sich auf veränderte Konfigurationen. Bzw. beziehen sie sich in Wirklichkeit auf "gestern ging es aber noch und ich habe nichts gemacht!", es lässt sich dann aber auf eine Konfigurationsänderung zurückführen. Beim Ändern des Tastatur-Layouts (mein Lieblingsbeispiel), noch nicht einmal einer bewussten Änderung.

Es ist nicht nur so, dass mich solche Art von Anfragen nerven, die Anwender fühlen sich auch noch Dumm. Frustration also auf beiden Seiten und eine Problemlösung in weiter Ferne.

Noch ein Gedanke zum zu wählenden Betriebssystem. Möchte man Long Term Support haben? Ich bin da etwas zwiegespalten. Persönlich setze ich Software ein, die noch an den Kanten blutet. Das ist aus meiner Sicht auch nicht der Königsweg, vor allem nicht für Anwender. Aber ist es LTS? Ich finde kürzere Release-Zyklen in sofern besser, dass sich in kleineren Abständen kleinere Dinge ändern, statt einer großen Änderung alle 3 Jahre. Man stelle sich nur das Szenario des Umstieges von Win XP mit Office 2003 und Internet Explorer 8 auf etwas neues vor. Wirklich jedes Programm hat sich in den Jahren gründlich geändert. Eigentlich möchte man als Admin ein wartungsfreies System. Aber ganz ehrlich. Das wird es nicht geben. Irgendwann wird neue Hardware nicht unterstützt, oder neue Programme laufen auf antiquierten Systemen nicht mehr. Über die Pflege alter Pakete und das ausmerzen von Sicherheitslöcher möchte ich erst gar nicht anfangen. In der DevOps-Bewegung werden lieber viele kleine iterative Rollouts von Software, gegenüber großen Big Bang Rollouts bevorzugt. Ich denke auch Desktop-Systeme können sich sinnvoll in diese Richtung entwickeln.

Ich finde das Benutzbarkeitsproblem mit/in Android und iOS ebenfalls noch nicht gelöst. Klar kann eine 100 Jahre alte Person sich Fotos auf dem iPad anschauen - und nach kurzem zeigen - vor und zurück fahren. Aber wenn dieser oder diese auch nur die Reihenfolge der Icons verändert hat, ist Holland in Not.

Über dieses Thema haben sich bestimmt schon schlauere Menschen als ich den Kopf zerbrochen. Ich war nur wieder einmal frustriert genug etwas ins Internet zu schreiben.

Kommentare: https://plus.google.com/111200602971512069193/posts/TDsUyxoXgjR

Autor
Chris Glaubitz
Configuring, coding, debugging computers for a living. Riding bikes for fun.

Cinnamon 2.4.6, change Alt-Tab/Control-Alt-Tab behaviour

I was annoyed of cinnamons Alt-Tab and Control-Alt-Tab handling. So I just switched it.

$ dconf write /org/cinnamon/desktop/keybindings/wm/switch-panels "['<Control><Alt>Tab']"
$ dconf write /org/cinnamon/desktop/keybindings/wm/switch-windows "['<Alt>Tab']"

Not Alt-Tab cycles all the windows of all workspaces. Control-Alt-Tab cycles the windows on the current workspace only.

Autor
Chris Glaubitz
Configuring, coding, debugging computers for a living. Riding bikes for fun.

oslo.messaging example

Just a little example how to use oslo.messaging without much around it. You need to have a running rabbitmq (apt-get install rabbitmq-server on ubuntu ;)).

Add user testing to rabbitmq

# rabbitmqctl add_user testing test

# rabbitmqctl set_permissions -p / testing '.*' '.*' '.*'

Setting up a virtualenv and installing oslo.messaging into it. You need to have python-dev installed.

$ virtualenv testing

$ . testing/bin/activate
$ pip install --upgrade pip # to be sure, pip is able to manage wheel
$ pip install --upgrade setuptools # for entry point discovery
$ pip install oslo.messaging

consumer part:

#!/usr/bin/env python

# coding: utf-8

from oslo.config import cfg
from oslo import messaging
import logging

import eventlet

eventlet.monkey_patch()

logging.basicConfig()
log = logging.getLogger()

log.addHandler(logging.StreamHandler())
log.setLevel(logging.INFO)

class NotificationHandler(object):
    def info(self, ctxt, publisher_id, event_type, payload, metadata):
        if publisher_id == 'testing':
            log.info('Handled')
            return messaging.NotificationResult.HANDLED

    def warn(self, ctxt, publisher_id, event_type, payload, metadata):
        log.info('WARN')

    def error(self, ctxt, publisher_id, event_type, payload, metadata):
        log.info('ERROR')

log.info('Configuring connection')
transport_url = 'rabbit://testing:test@10.0.80.12:5672/'
transport = messaging.get_transport(cfg.CONF, transport_url)

targets = [messaging.Target(topic='monitor')]
endpoints = [NotificationHandler()]

server = messaging.get_notification_listener(transport, targets, endpoints, allow_requeue=True, executor='eventlet')
log.info('Starting up server')
server.start()
log.info('Waiting for something')
server.wait()

producer part:

#!/usr/bin/env python

# coding: utf-8

from oslo.config import cfg
from oslo import messaging
import logging

logging.basicConfig()
log = logging.getLogger()

log.addHandler(logging.StreamHandler())
log.setLevel(logging.INFO)

transport_url = 'rabbit://testing:test@10.0.80.12:5672/'
transport = messaging.get_transport(cfg.CONF, transport_url)

driver = 'messaging'

notifier = messaging.Notifier(transport, driver=driver, publisher_id='testing', topic='monitor')

notifier.info({'some': 'context'}, 'just.testing', {'heavy': 'payload'})
Autor
Chris Glaubitz
Configuring, coding, debugging computers for a living. Riding bikes for fun.

OpenStack + LXC + OpenContrail

Update: Patch is available in nova-contrail-vif-driver .

 

The last two days I worked on the integration of LXC into OpenContrail and OpenStack (icehouse). OpenStack does not support LXC in the first place. However, it is still included into nova and working via the libvirt driver. So I edited /etc/nova/nova.confand enabled lxc for libvirt.

[libvirt]

vif_driver=nova_contrail_vif.contrailvif.VRouterVIFDriver
virt_type=lxc

First I just tried to boot a usual kvm-Image, and it turned out, that it almost works. For lxc the image is mounted via qemu-nbdsomewhere to /var/lib/nova/instances/7849061d-740f-4727-9fa8-eca84bb3d77b/rootfs, but nova does not take care about partitions. So the mount failed:

2014-08-22 14:37:28.456 28050 ERROR nova.virt.disk.api [req-4982f8a8-cb29-4080-a0be-3a40d5a3e197 eb4ba78065e04df194d3a28f98a6eeac eaef37b7d8c24725bfc8c4e4090b4d97] Failed to mount container filesystem '<nova.virt.disk.api._DiskImage object at 0x43d5350>' on '/var/lib/nova/instances/77d6a580-eaba-4e8f-99e2-f9810df74f24/rootfs':

--
Failed to mount filesystem: Unexpected error while running command.
Command: sudo nova-rootwrap /etc/nova/rootwrap.conf mount /dev/nbd8 /var/lib/nova/instances/77d6a580-eaba-4e8f-99e2-f9810df74f24/rootfs
Exit code: 32
Stdout: ''
Stderr: 'mount: block device /dev/nbd8 is write-protected, mounting read-only\nmount: you must specify the filesystem type\n'

Not much to do to fix this. Edit /usr/lib/python2.7/dist-packages/nova/virt/disk/api.pyline 380 and add partition=1:

img = _DiskImage(image=image, use_cow=use_cow, mount_dir=container_dir, partition=1)

Restarting nova-compute. The image can be mounted, but the instance will not start because of the network anyway.

I created a VIF Driver to install necessary devices. It just bases on the Nova Driver of OpenContrail, but uses bridges instead of tap-devices. https://github.com/chrigl/nova-lxc-contrail-vif

Install this driver:

$ git clone git@github.com:chrigl/nova-lxc-contrail-vif.git

$ cd nova-lxc-contrail-vif
$ pip install .

And enable it in /etc/nova/nova.conf:

[libvirt]

vif_driver=nova_lxc_contrail_vif.contrailvif.VRouterVIFDriver
virt_type=lxc

Basically thats it. The instances should spawn and get an internal ip address. If they are in the same network, they should reach each other.

This is totally just a proof of concept, and I didn't do any tests within those containers. So I don't know anything about the forced limitations, like RAM, CPU or disk space, yet.

However. It was fun (and a bit painful for sure) digging into several parts of OpenContrail, libvirt, lxc and OpenStack. After the unsuccessful tests in OpenStack, I decided to start from the bottom up. First I installed lxc to get a feeling for how it is working, how limits are set, and especially how network interfaces are assigned to a container. Hint. Yes, it is possible to assign bare ethernet devices into a container. I will describe later, why I had to implement the stuff using brigdes.

Stepping one layer up, I defined some LXCs by using bare libvirt without any OpenStack on top. First using the libvirt default network, then using bridges, because it is well documented. Trying to assign a real ethernet device failed, since this is not yet supported by libvirt o_O There is already a patch for libvirt: http://www.redhat.com/archives/libvir-list/2014-February/msg00234.html

I learned much so far. The next step for me, was having a look into the OpenContrail network driver for nova. By default this driver creates tap devices and pass them into kvm-VMs via virtio and passthrough. However, as I learned so far. Libvirt is not able to pass a network device to LXCs directly. So I came up with the idea, just setting a bridge onto the tap device. This didn't work in the first place either, since OpenContrail decides about the state of the VM by getting the state of the tap device (UP|DOWN). But since the LXC was not running directly ontop of this device, it was DOWN all the time. Even if i tried to enable it with ip link set tabXYZ up. I started tcpdumpand saw data on my created bridge, but not on the device, assigned to the bridge!? So the really really last try for today was, registering the bridge instead of the tap device to OpenContrail. And it magically worked!

Fun fact: After assigning the bridge to OpenContrail, the created LXCs behaved strange. Really strange. So they start, but didn't find the metadata service. It felt like sometimes it worked and sometimes not. While debugging with pinging the gateway ip, it turned out, network works if tcpdump is running. If I stopped my tcpdump session, the ping stucks. It took me some time to realize tcpdump is switching promiscous mode on start and end. The quite simple solution: turning permiscous mode on by default.

[update] If you build your own images make sure it contains /sbin/init. nova uses this as init.

<domain type='lxc' id='22415'>

  [...]
  <os>
    <type arch='x86_64'>exe</type>
    <init>/sbin/init</init>
    <cmdline>console=tty0 console=ttyS0 console=ttyAMA0</cmdline>
  </os>
  [...]
</domain>
Autor
Chris Glaubitz
Configuring, coding, debugging computers for a living. Riding bikes for fun.

EuroPython 2014 - Tag 5

Heute ist der letzte Tag der diesjährigen EuroPython.

Gestartet bin ich mit dem Vortrag Everything You Always Wanted to Know About Memory in Python But Were Afraid to Ask von Piotr Przymus . Er beschreibt, dass sich vermutlich jeder, der c/c++ lernen musste, mit den Größen der verschiedenen Datentypen beschäftigt hat. In Python ist dies nicht der Fall. Auch ich habe mir bisher keine großen Gedanken darüber gemacht. Piotr zeigte die Größen von ein paar built-in Datentypen. Wegen des Overheads für den Garbage Collector fallen diese immer etwas opulenter aus als in c. Pjotr ist darauf eingegangen wie Python versucht die selben Werte für unterschiedliche Variablen nur einmal zu benuzten. Z.B. a=0, b=0, a is b ==> True. Wie das für Strings forciert werden kann, wie Listen und Dictionaries in Punkto preallocation arbeiten usw.

Der Talk Advanced Database Programming with Python von Marc-Andre Lemburg hat mein Vorurteil bestätigt, dass Entwickler und Datenbanken nicht so besonders gut zusammen passen. Es war zwar nicht alles Blödsinn, doch hat man einfach die Entwicklersicht auf die Dinge gemerkt. Die Krönung war Schlicht die Empfehlung zu random-IDs, statt auto_increment zu verwenden. Meine Gedanken dazu warum das für gewöhnlich eine schlechte Idee ist.

Schlomo Schapiro hat einen weiteren Talk aus dem ImmobilienScout24 Umfld und seiner persönlichen Erfahrung gemacht. Er beschrieb welche Möglichkeiten es für Unternehmen gibt mit open source software zu arbeiten und vor allem dazu beizutragen. ImmobilienScout24 hat schon häufig die Entwickler eines Projektes für Support bezahlt. Magischer Weise ist bei diesem Support auch ein Feature hinten raus gefallen. Wichtig ist Code erweiterbar zu gestalten, so dass es möglich ist auch Features aufzunehmen, die vielleicht nur einen extremen Spezialfall abdecken, dafür aber niemand anderes stören. Als Beispiele nannte Schlomo subversion und icinga.

Im Nachmittagprogramm habe ich mich mit ein paar Leuten zusammen gesetzt und wir haben einen Blick auf den Nix package manager geworfen. Dabei habe ich ein docker image mit Nix auf Basis des busybox-Images erstellt. Darauf aufbauend könnte jeder von Nix verteilte Service installiert und ausgeliefert werden. Vielleicht wäre das eine einfache Möglichkeit eine Applikation inklusiver aller Abhängigkeiten zu verteilen, bei der vor allem auch neuere Software als z.B. in den Ubuntu Cloud Images benutzt/benötigt wird.

Autor
Chris Glaubitz
Configuring, coding, debugging computers for a living. Riding bikes for fun.