Meine erste Reise bei NETWAYS!

Letzte Woche war es soweit ich durfte meine erste Dienstreise antreten. Zwar handelte es sich hierbei nur um eine Schulung, aber immerhin. Es ging nach München für die Icinga 2 Fundamentals Schulung.

Mit Lennart als Trainer, ging es Montag Nachmittag los. Zwei Stunden später waren wir angekommen. Natürlich mussten wir noch den Schulungsraum auf Vordermann bringen. Tische zurecht rücken, mit Notebooks bestücken, Unterlagen verteilen, Stromanschluss bereitstellen und so weiter. Der Aufbau nahm schon etwas Zeit in Anspruch, aber zu zweit ging das ganze doch schneller als erwartet. Ab aufs Zimmer um für den nächsten Tag fit zu sein, denn um neun Uhr geht es los. Themen der Icinga 2 Fundamentals Schulung? Wie der Name schon erwarten lässt, Grundkentnisse und Grundlagen zu Icinga2. Doch wer Lennart kennt, weiß dass er in den gegebenen Tagen meist mehr schafft als geplant.

Also eine erholsame Nacht im Hotel später wurden die Schulungsteilnehmer in Empfang genommen. Um acht Uhr trafen die ersten bereits ein um ihre Plätze einzunehmen. Nach einer kleinen Vorstellungsrunde bei der sich Lennart mit dem derzeitigen Wissenstand der Teilnehmer auseinandersetzte sind wir auch schon gleich mit Icinga2 eingestiegen. Egal ob Icinga selbst, einfache Plugins oder der Icinga Director es wurde alles abgedeckt.

Selbstverständlich gab es reichlich Pausen, sowie Getränke und Essen zu genüge. Auf leeren Magen lässt es sich schlecht lernen und die Worte NETWAYS und leerer Magen sind nicht miteinander vereinbar. Am zweiten Schulungstag wurde es gegen Abend mal Zeit für einen Tapetenwechsel und wir machten uns auf dem Weg zum Augustiner Klosterwirt. In geselliger Atmosphäre in diesem rauen Etablissement wurde der Abend ausgeklungen und nach einem deftigen Abendessen auch das ein oder andere Bier getrunken.

Die vier Tage gingen schnell vorüber. Für mich als zukünftigen Consultant war es praktisch zu sehen, wie Lennart den Schulungsteilnehmern individuell zur Seite stehen konnte und noch während der Schulung Auskunft gegeben konnte über die Anwendung in den entsprechenden Umgebungen der Kunden. Es waren vier sehr intensive Tage vollgestopft mit sehr viel Informationen. Seit Freitag bin ich wieder in Nürnberg und jetzt heißt es erst einmal sacken lassen. Wird bestimmt nicht lange dauern bis ich mein erstes Icinga-Projekt bekomme.

Um ehrlich zu sein, war ich schon ein bisschen nervös. Vor der ersten richtigen Dienstreise, aber jetzt kann ich die nächste kaum erwarten.

Tobias Bauriedel
Tobias Bauriedel
Junior Consultant

Tobias ist ein offener und gelassener Mensch, dem vor allem der Spaß an der Arbeit wichtig ist. Bei uns macht er zurzeit seine Ausbildung zum Fachinformatiker. In seiner Freizeit ist er viel unterwegs und unternimmt gern etwas mit Freunden.

Generics waren gestern. Lang lebe Golangs Reflection!

Vor einiger Zeit habe ich die Programmiersprache Golang und all ihren Nutzen für die Entwickler vorgestellt. Zugegeben, eine in der Konkurrenz sehr verbreitete Funktionalität besitzt Go nicht: Generics. Und diese Funktionalität is noch dazu sehr gefragt. Allein das Gesamtbild der Reaktionen (Smileys) auf den Vorschlag, Generics in Go v2 zu integrieren, sagt mehr als 1000 Worte. Aber es geht auch anders…

Problem

Aktuell arbeite ich an einem (streng geheimen) Programm, das u.a. mit SQL-Datenbanken kommunizieren soll. Die grundsätzliche Infrastruktur dafür bringt Go von Haus aus mit. Jedoch kann es etwas mühselig sein, bei jeder Abfrage dieselbe Routine (samt Fehlerbehandlung) durchzukauen:

package blog

import (
	"database/sql"
)

type Employee struct {
	GivenName, FamilyName string
}

func GetEmployees(db *sql.DB) ([]Employee, error) {
	rows, errQuery := db.Query("SELECT given_name, family_name FROM employee")
	if errQuery != nil {
		return nil, errQuery
	}

	defer rows.Close()

	employees := []Employee{}

	for {
		if rows.Next() {
			row := Employee{}

			if errScan := rows.Scan(&row.GivenName, &row.FamilyName); errScan != nil {
				return nil, errScan
			}

			employees = append(employees, row)
		} else if errNext := rows.Err(); errNext == nil {
			break
		} else {
			return nil, errNext
		}
	}

	return employees, nil
}

Außerdem hat sich in einem vergangenen Projekt herausgestellt, dass (zumindest in Transaktionen) erst nach rows.Close() die nächste Datenbank-Operation beginnen kann. Dies verpflichtete fast schon dazu, den Code ab defer db.Close() bei jeder Abfrage so oder so ähnlich zu schreiben. Letztendlich löste das Team das Problem mit folgender Funktion:

func FetchAll(db *sql.DB, query string, args ...interface{}) ([][]interface{}, error)

Diese erledigte die oben gezeigte Routinearbeit und verringerte damit den Aufwand pro Abfrage deutlich:

func GetEmployees(db *sql.DB) ([]Employee, error) {
	rows, errFetchAll := FetchAll(db, "SELECT given_name, family_name FROM employee")
	if errFetchAll != nil {
		return nil, errFetchAll
	}

	employees := []Employee{}

	for _, row := range rows {
		employees = append(employees, Employee{row[0].(string), row[1].(string)})
	}

	return employees, nil
}

Jedoch war nun jede Spalte jeder Zeile des Ergebnisses ein interface{}, das erstmal in den richtigen Datentyp umgewandelt werden musste. Dafür wiederum musste die neue Funktion zusätzlich den Spaltentyp beim Datenbanktreiber erfragen, um immer die (hinter dem interface{} versteckten) Datentypen zurückzugeben, die die konkrete Abfrage erwartet. Andernfalls hätten wir uns auf die Standard-Datentypen der Datenbanktreiber verlassen müssen.

Lösung

Nun übernehme ich also den Code Schritt für Schritt in das neue Projekt und frage mich: Geht das nicht auch einfacher? Ja, mit sog. Reflection:

package blog

import (
	"database/sql"
	"reflect"
)

func FetchAll(db *sql.DB, rowType interface{}, query string, args ...interface{}) (interface{}, error) {
	rows, errQuery := db.Query(query, args...)

	if errQuery != nil {
		return nil, errQuery
	}

	defer rows.Close()

	blankRow := reflect.ValueOf(rowType)
	res := reflect.MakeSlice(reflect.SliceOf(blankRow.Type()), 0, 0)
	idx := -1
	scanDest := make([]interface{}, blankRow.NumField())

	for {
		if rows.Next() {
			res = reflect.Append(res, blankRow)
			idx++

			row := res.Index(idx)

			for i := range scanDest {
				scanDest[i] = row.Field(i).Addr().Interface()
			}

			if errScan := rows.Scan(scanDest...); errScan != nil {
				return nil, errScan
			}
		} else if errNext := rows.Err(); errNext == nil {
			break
		} else {
			return nil, errNext
		}
	}

	return res.Interface(), nil
}

Diese Funktion erwartet einen zusätzlichen Parameter, rowType. Dessen eigentlicher Typ hinter interface{} (Employee) bestimmt den Typ einer Zeile des Abfrage-Ergebnisses. Das komplette Ergebnis ist logischerweise eine Slice aus Zeilen ([]Employee). Mit Hilfe von Funktionen aus dem reflect-Paket arbeitet FetchAll() zur Laufzeit mit dem konkreten Datentyp Employee, fast so als wäre er mittels Generics zur Kompilierzeit bekannt:

  • reflect.ValueOf(rowType) analysiert rowType und kapselt ihn als Wert vom Typ Employee
  • reflect.ValueOf(rowType).Type() steht für Employee
  • reflect.SliceOf(Employee) steht für []Employee
  • reflect.MakeSlice([]Employee, 0, 0) steht für make([]Employee, 0, 0)
  • reflect.ValueOf(rowType).NumField() zählt die Felder des Structs Employee

Ja, richtig, rowType muss ein Struct sein, sonst stürzt das Programm spätestens bei reflect.ValueOf(rowType).NumField() ab. Jedes Feld des Structs steht nämlich für eine Spalte des Abfrage-Ergebnisses. Genau das wird in der darauf folgenden Schleife wie folgt bewerkstelligt:

  • res = reflect.Append(res, reflect.ValueOf(rowType)) steht für res = append(res, Employee{})
  • res.Index(idx) steht für res[idx]
  • res[idx].Field(0) steht für res[idx].GivenName
  • res[idx].GivenName.Addr() steht für &res[idx].GivenName

Und .Interface() holt letztendlich den Zeiger auf das Struct-Feld aus der Reflection-Versenkung, damit rows.Scan() die entsprechende Spalte des Abfrage-Ergebnisses darin speichert. Am Ende verbirgt sich hinter res tatsächlich ein []Employee, das mit res.Interface() in ein interface{} gekapselt, um es zurückzugeben. Damit bestimmt GetEmployees() den Zeilen-Typ im voraus und schrumpft auf ein vernünftiges Minimum:

func GetEmployees(db *sql.DB) ([]Employee, error) {
	rows, errFetchAll := FetchAll(db, Employee{}, "SELECT given_name, family_name FROM employee")
	if errFetchAll != nil {
		return nil, errFetchAll
	}

	return rows.([]Employee), nil
}

Fazit

Nachdem ich zuletzt schon eine C-Bibliothek in Go wiederverwendet habe, spare ich schon zum zweiten mal in Folge Code und damit Zeit. Sprich, wir arbeiten jetzt noch ein bisschen effizienter (als sowieso schon) an euren Projekten. Bestelle noch heute!

Alexander Klimov
Alexander Klimov
Developer

Alexander hat 2017 seine Ausbildung zum Developer bei NETWAYS erfolgreich abgeschlossen. Als leidenschaftlicher Programmierer und begeisterter Anhänger der Idee freier Software, hat er sich dabei innerhalb kürzester Zeit in die Herzen seiner Kollegen im Development geschlichen. Wäre nicht ausgerechnet Gandhi sein Vorbild, würde er von dort aus daran arbeiten, seinen geheimen Plan, erst die Abteilung und dann die Weltherrschaft an sich zu reißen, zu realisieren - tut er aber nicht. Stattdessen beschreitet er mit der...

Terraform Changes

Hallo!

Was vielen unseren von geneigten Lesern entgeht ist das wir auch in unserem Alltag zwischen der ganzen Softwareschreiber- und Kompilier Tätigkeiten auch ganz viele virtuelle Maschinen und Container zum testen selbiger Software rauf und runter installieren müssen und das Tag für Tag.

Deshalb und gerade deshalb versuchen wir uns das Leben mit dafür erstellter Software und deren arbeitserleichternden Funktionen leichter zu machen. Wie mein Kollege schon in seinem Blogpost im März mir vorgegriffen hat benutzen wir bei der Netways Terraform. Achim wird in weiteren Artikeln darauf eingehen wie man Openstack per Terraform nach seiner Pfeife tanzen lassen kann und Ich möchte mich heute auf ein anderes Terraform Thema beziehen nämlich dem nahen release von Terraform 0.12.

Zu dem Zeitpunkt wo ich diese Zeilen niederschreibe ist auf der aktuellen Website von Hashicorp noch die Aktuelle Version 0.11.13 zu finden.

Aber Terraform hat schon etwas den Vorgang gelüftet und uns vielversprechendes gezeigt mit dem 0.12.0-beta1 pre-release.

Damit kann man schon die viele Erleichterungen welche der neue Terraform release mit sich bringt erahnen und auch schon antesten. Ich versuche mich an einer Erleuterung den änderungungen Anhand eines kleinen Beispiels.
Vielleicht kann ich den einen oder anderen IaaS Codeschreiber welcher sich hierfür interessiert etwas auf den Geschmack zu bringen schon etwas zu testen mit der neuen Version.

Hier der Unterschied zwischen einer (aktuell 0.11.13) alten Terraform Version und einer neuen Version in Terraform 0.12 durch eine Gegenüberstellung.

main.tf (Random Tiernamen Beispiel)

variable "count" { default = 1 } variable "default_prefix" { default = "Giraffe" } variable "zoo_enabled" { default = "0" } variable "prefix_liste" { default = [] } resource "random_pet" "my_pet" { count = "${var.count}" prefix = "${var.zoo_enabled == "0" ? var.default_prefix : element(concat(var.prefix_liste, list (""), count.index)}" }

main.tf HCL2 Version(Random Tiernamen Beispiel)

variable "pet_count" { default = 1 } variable "default_prefix" { default = "Giraffe" } variable "prefix_list" { default = [] } resource "random_pet" "my_pet" { count = var.pet_count prefix = var.zoo_enabled ? element(var.prefix_list, count.index) : var.default_prefix }

Die Unterschiede fallen zuerst etwas weniger ins Auge sind aber dafür meines Erachtens tiefgreifender für Leute die IaaS Code schreiben müssen und es dient der Lesbarkeit des Codes.

Vorteil Nummer 1:
Im alten Beispiel musste noch mit “${var.count}” von einem String zu einer Number evaluiert werden, mit der neuen HCL2 schreibweise entfällt das und es kann mit var.pet_count direkt der korrekte String oder Number Wert adressiert werden.

Vorteil Nummer 2:
Auch die Evaluierung der Liste prefix = “${var.zoo_enabled == “0” ? var.default_prefix : element(concat(var.prefix_liste, list (“”), count.index)}”  wird mit der neuen notation der HCL2 wesentlich eingängiger. prefix = var.zoo_enabled ? element(var.prefix_list, count.index) : var.default_prefix ist prägnanter. Es entfällt auch die element(concat(x), list(“”), x ) Hack-Situation um aus einer leeren Liste auch eine Liste mit einem NULL Element zum machen.

Vorteil Nummer usw. es gibt viel mehr was geändert worden ist, if you want to know more here.

Ich hoffe ich habe euch nicht zu sehr gelangweilt mit C.O.D.E. kurz vor dem Wochenende.

Gruß David

 

David Okon
David Okon
Senior Consultant

Weltenbummler David hat aus Berlin fast den direkten Weg zu uns nach Nürnberg genommen. Bevor er hier anheuerte, gab es einen kleinen Schlenker nach Irland, England, Frankreich und in die Niederlande. Alles nur, damit er sein Know How als IHK Geprüfter DOSenöffner so sehr vertiefen konnte, dass er vom Apple Consultant den Sprung in unser Professional Services-Team wagen konnte. Er ist stolzer Papa eines Sohnemanns und bei uns mit der Mission unterwegs, unsere Kunden zu...

PHP-Frameworks – Was sie sind und für warum sie gebraucht werden.

Im Rahmen eines Projekts woran ich selber arbeiten darf, bin ich oftmals auf den Begriff Framework gestoßen und habe mir dann erklären lassen für was wir eigentlich genau dieses Framework benutzen und warum kein anderes.
Daher möchte ich ein paar Frameworks aufführen, sie zusammenfassen und natürlich Kund darüber geben, was sie machen.
Zu allererst möchte ich mit der allgemeinen Defintion von „Framework“ anfangen. Let‘s google it.
Ein Programm-Code besteht aus vielen kleinen Bausteinen. Um das Programmieren zu erleichtern und Zeit zu sparen, gibt es sogenannte Frameworks. Sie sind keine eigenständigen Programme, sondern stellen ein Programmiergerüst für den Entwickler.

Nun kommen wir zum Smart Framework.
Hierbei handelt es sich um ein Open-Source-Produkt, welches dem Model-View-Controller-Datenmodell folgt. Dieses Projekt wurde 2009-2011, von einem Software-Unternehmen, das bereits 15 Jahre Erfahrung im Web hat, gestartet. Der Release-Termin war im Februar 2012 und es wird bis dato noch relativ oft aktualisiert. Der letzte Stand der Aktualisierung ist der 25. April 2018 mit der Version 3.7.5. Zu den wichtigsten Funktionen gehört die reichhaltige Unterstützung verschiedener Typen von Datenbanken wie MySQL, SQLite, MongoDB, PostgreSql sowie andere JavaScript-Komponenten, integrierte Map-Handler(Google Maps und Bing Maps) und Redis für das Caching.
Unter anderem meint der Ersteller des Frameworks, dass dieses um einiges schneller ist als andere Frameworks wie Laravel und Zend. Ein Grund es mehr auszuprobieren.

 

wurde 2005 von Fabien Potencies, CEO bei SensioLabs und Blackfire.io, gestartet. Symfony besteht aus mehreren PHP-Komponenten, mehr als 30, die ein Framework bilden. All diese Komponenten hier aufzulisten, wäre einfach zu viel aber unter diesen 30 erweisen sich ein paar als interessant und es lohnt sich diese hier zu erwähnen. Dazu gehört unter anderem die Cache-Komponente, die ein erweitertes PSR-6 und PSR-16 zum Hinzufügen von Cache zu den Anwendungen bereitstellt. Konsolenkomponenten, die selbsterklärend sind, und eine PHPUnit Bridge, welche Legacy-Tests und veralteten Code meldet. Zuletzt noch Sicherheits-, Übersetzungs-, Routing- und Validierungskomponenten. Ebenfalls einzigartig ist die Dokumentation. Diese ist von dem Ersteller des Projekts sehr detailliert ausgeführt und es wird jede einzelne Komponente erklärt.
 

TwistPHP
Dieses Framework wurde zuerst als privates Projekt entworfen und hat sich dann während der Verbesserung, im Juli 2014 als Repository im GitHub wieder gefunden. Die erste offizielle Freigabe war aber wiederum im November 2014.
TwistPHP macht die Benutzerfreundlichkeit zu seinem Markenzeichen, da es ein kleines Framework ist. Hierbei handelt es sich auch wieder um ein Open-Source-Framework, welches unter der GNU-Lizenz angeboten wird.
Im Laufe der Zeit hat sich die Software weiterentwickelt und hat letztendlich mit der Version (3.0.5) eine vollständige MVC-Architektur, ein objektorientiertes Design, eine brandneue Methode zur Verbindung mit der Datenbank und zur Erstellung von MySQL-Abfragen, wobei die Art und Weise, wie es aufgebaut wurde, die Erweiterbarkeit und Zuverlässigkeit verbessert.


Fangen wir am Ursprung an. TYPO3 ist ein open-source-entwickeltes CMS, welches 1998 veröffentlicht wurde. TYPO3 wird genutzt um Websiten zu erstellen und wurde von mehr als 300 Mitwirkenden verbessert und aktuell wurde es mehr als 500.000 Mal installiert (Stand Januar 2019). Das Team von TYPO3 wollte ein modernes Produkt schaffen, welches unabhängig von TYPO3 sein sollte. Im August 2011 folgte dann die erste Beta von TYPO3 Flow sowie ein Open-Source-Produkt. Das Prinzip des Codings, vom neuen Framework, richtet sich nach dem MVS-Paradigma, Aspect Oriented Programming, Domain Driven Desgin und den Test Driven Development. Fluid, ein Template, ist ein weiteres Feature von TYPO3 Flow. Fluid unterstützt alle logischen Strukturen einer Programmiersprache wie zum Beispiel Iterationen und Schleifen usw., indem es eine einfache Syntax bietet und PHP in den Template-Dateien vermeidet.

Zu guter Letzt eines der beliebtesten Frameworks das:
Der Beliebtheitsgrad lässt sich anhand der hohen Installationszahl (mehr als 379 Millionen) festhalten, obwohl es einige Streitigkeiten über Zend gibt. Das Open-Source-Projekt wurde im März 2006 erstmals veröffentlicht und hat eine BSD-Lizenz (Freie Verwendung der Software). Nach Version 2.5 beschlossen die Entwickler ein modulares Produkt zu entwerfen, sie so in mehrere Komponenten aufzuteilen und dadurch es zu einer Sammlung von PHP-Paketen wurde. Mehr als 60 Komponenten und ein Plug-in stehen zur Auswahl. Darunter gehört die Authentifizierung, Grypt, Json, Mail, Math, Paginator, Serializer und Validator. Ein weiterer Vorteil der Verwendung von Zend ist die Prädisposition für das Test Driven Development. Zend implementiert Zend_test, was PHPUnit verwendet. Es erlaubt einem Controller, Modelle und Bibliotheken zu verwenden. Um PHPUnit in das Projekt zu implementieren, braucht man Zend_tool, das standardisierte Scaffolding Utility von Zend.

Fazit
Auf die Frage „Und welches ist jetzt das beste PHP-Framework?“ gibt es keine Antwort aber wenn man vor der Auswahl steht und nicht weiß welches Framework man nun benutzen soll helfen folgende Fragen.

  • Was will ich entwickeln?
  • Wann ist meine Deadline?
  • Wie schnell soll mein Projekt verfügbar sein?
  • Wie viel Zeit bleibt mir, um die neue Syntax zu erlernen?
  • Gibt es eine gute Dokumentation, aus der ich lernen kann?

Hilfreich ist es diese Fragen sich vor jedem Projekt zu stellen und dann zu schauen welches Framework den nun den gestellten Fragen Antwort leistet.

Niko Martini
Niko Martini
Junior Developer

Egal ob zu Hause oder bei NETWAYS, Niko hockt gern vor dem PC. Ab und zu fährt er auch mal mit seinem Dad auf eine Fahrradtour quer durch Deutschland. Nach seinen ersten Tagen bei uns, in denen er NETWAYS, die Kollegen und Tools näher kennengelernt hat, freut er sich besonders auf die kommenden Jahre.

Partition voll, Wo?

Dieses Problem kennt bestimmt jeder SysAdmin, das Monitoring meldet Partition fast vollgelaufen, gut die Partition weiß man schon mal, laut Monitoring Icinga


Das heißt aber noch nicht automatisch WO die Partition vollgelaufen ist. In Zeiten wo Storage-Systeme, SSD’s und Speichermedien immer größer werden, werden auch die Dateien immer größer und Speicherplatz nicht unendlich ist, werde ich heute mal mit ein paar einfachen Shell-Kommandos auf einem Linux-System zeigen, wie schnell man das Verzeichnis ermittelt, das vollgelaufen ist.
Tool meiner Wahl ist dafür:
du -> Disk Usage
Größe gesamt summiert
du -hs /
49G /

Möchte ich mir einen Überblick über das File-System mit allen Ordnern sehen, damit ich weiß in welchen dieser Ordner am vollsten ist:
du -hx / -d1
36M /etc
43M /root
47G /var
3,2M /tmp
1,4G /usr
300M /boot
1,9M /home
0 /media
0 /mnt
22M /opt
0 /srv
48G /

Der Schalter -h (human readable) und -d1 (nur die erste Ordner-Reihe ohne Unterverzeichnisse), Schalter -x, (nur in diesem Filesystem suchen)

Erkenntnis: Aha, im Verzeichnis /var sind sehr viele Daten, also Schlussfolgerung, in diesem Verzeichnis weiter suchen.
# du -hx /var -d1
4,0K /var/tmp
31G /var/lib
211M /var/log
16G /var/adm
119M /var/cache
8,0K /var/db
47G /var

Das ganze kann ich jetzt soweit fortführen, bis ich das gewünschte Verzeichnis gefunden habe und entscheiden, was gelöscht / ausgelagert werden kann oder welche Anwendung ich optimieren muss, um den Speicherplatz wieder freizugeben.

In meinem Fall, war es die MySQL-Datenbank, die mir hier den Platz raubt.
# du -hx /var/lib/ -d1 | grep mysql
31G /var/lib/mysql

Lösung: z.B. VM / Partition vergrößern, Datenbank auslagern auf externen Storage.

Dieses Tool kann noch mehr, am besten ausprobieren(learining by doing), die Man-Page man du verrät noch mehr.

Wir bieten auch Trainings zu vielen Themen rund um Open Source an, Schau mal rein.

Johannes Carraro
Johannes Carraro
Support Engineer

Bevor Johannes bei NETWAYS anheuerte war er knapp drei Jahre als Systemadministrator in Ansbach tätig. Seit Februar 2016 verstärkt er nun unser Managed Services Team als Systems Engineer. In seiner Freizeit spielt Johannes E-Gitarre in einer Metalband, bastelt an Linux Systemen zuhause herum und ertüchtigt sich beim Tischtennisspielen im Verein, bzw. Mountainbiken, Inlinern und nicht zuletzt Skifahren.

Aufmerksamkeit – ein super Mittel gegen schädliche Emails

Vor etwa einem halben Jahr stand der Autor im Kontakt mit einer lokalen Metzgerei im Vorwahlbereich 0911. Es ging um leckere Weißwürste mit ebenso leckeren Brezen.

Diese wurden geliefert und NETWAYS-typisch mit vollem Einsatz verzehrt.

Wir nehmen an, es wäre Mittwoch, 20.02.19, 09:19. Emaileingang am persönlichen Postfach:

Erstmal verblüffend:

a) Ich hatte keine Rechnungskopie angefordert.

b) Der Absender hat nichts mit der Metzgerei meines Vertrauens zu tun – die zensierte Emailadresse in der Signatur wäre allerdings korrekt.

c) Die Vorwahl in der Signatur zeigt ins Nirvana

Nun gut, aber man will ja ein verlässlicher Geschäftspartner sein, eventuell liegt es ja nur am “von meinem Samsung gesendet”?

Also klicken wir fröhlich den Link und landen auf einer Seite aus Beheshti Avenue,Tehra,1577837414,Iran??

Außerdem wird eine .doc-Datei angeboten. Auch die kleinste Metzgerei hat es mittlerweile hinbekommen, Rechnungen per pdf zu verschicken.

Aber gut, schauen wir doch mal, was LibreOffice damit so anfängt:

Hier habe ich dann das Experiment abgebrochen.

Es waren zuvor schon viele “Red Flags”, die einen aufmerksamen Emailbenutzer stutzig machen sollten.

Und im Zweifelsfall kann man seinen Geschäftskontakt auch schlicht anrufen, wenn man denn auf Nummer Sicher gehen will.

Wer sich weitere Beispiele anschauen möchte, kann bspw. hier klicken.

edit (TA): typos

Tim Albert
Tim Albert
System Engineer

Tim kommt aus einem kleinen Ort zwischen Nürnberg und Ansbach, an der malerischen B14 gelegen. Er hat in Erlangen Lehramt und in Koblenz Informationsmanagement studiert, wobei seine Tätigkeit als Werkstudent bei IDS Scheer seinen Schwenk von Lehramt zur IT erheblich beeinflusst hat. Neben dem Studium hat Tim sich außerdem noch bei einer Werkskundendienstfirma im User-Support verdingt. Blerim und Sebastian haben ihn Anfang 2016 zu uns ins Managed Services Team geholt, wo er sich nun insbesondere...