Christoph Stoettner
stoeps@vegardit.com
@stoeps@infosec.exchange
Christoph Stoettner stoeps@vegardit.com
Macht seit 30 Jahren was mit Computern
Amiga, OS/2, Linux
Beruflich auch Windows (wenn es sein muss)
Started with Linux / OSS around 1994/1995
Linux Kernel < 1.0
Slackware
mag vi
, vim
, neovim
zu doof für emacs
Durchsuchbar / Volltextindex
Änderungen von allen Endgeräten
Smartphone, Tablet, Notebook, Customer Notebook
Server per SSH?
Vorlagen um Kundenanforderung oder CI umzusetzen
Offline Support
Versionskontrolle
Einstellungen aus XML und Property Dateien
Kein manuelles Kopieren von Dateien und Einstellungen
Automatisierte Erstellung von Ausgabeformaten
Editieren mit mobilen Geräten kompliziert
Wo dokumentiert man das Wiki
Installation
Konfiguration
Kein Zugriff falls Wiki nicht läuft
Kein einheitlicher Syntax
Marketing, Projekt oder Kunde fordern of gedruckte Dokumentation mit Vorlage
UND jede Menge Copy & Paste
Ich habe mit Markdown begonnen
Immer mehr Tools unterstützen Markdown Syntax
Cool WYSIWIG editors
Sehr gut für mobile Notizen
Aber etwas zu einfach
Dateien einbinden nicht möglich
Was mache ich?
Installation von Software auf Basis von Kubernetes und WebSphere Application Server
Jede Menge Pfade und Benutzer
Secrets, Passwörter und Datei-Konfiguration
XML, JSON, .properties …
Inzwischen mache ich viele Installationen mit Ansible oder automatisiert mit Bash Skripten
Manuell sollte ein Kunde allein auch eine Installation neustarten, troubleshooten und administrieren können
Die benötigten Log-Pfade, Admin-URL, Kommandos zur Konfiguration sind systemabhänging
Asciidoc ist die Sprachdefinition
Asciidoctor: Ruby project to convert asciidoc source to * output
Nur-Text, auf jedem Gerät lesbar
Versionskontrolle mit git
Unterschiede zu Markdown
Viele Ausgabeformate
HTML
Reveal.js
epub
docbook
:numbered: (1)
== H2
=== H3
==== H4
:!numbered: (2)
== H2
=== H3
==== H4
1 | nummerierte Überschriften |
2 | Überschriften ohne Nummerierung |
.Heading (1)
[source, bash]
----
ls -al
----
.Image (2)
image::test.png[role=fill]
1 | Heading above Code block |
2 | Image heading |
// some comment
def text():
var = xyz
x = x +1
==== Bullet Points
* Bullet Points
** Sub Bullet Point
==== Numbered Lists
. Numbered List
.. Sub 1
.. Sub 2
. Numbered 2
==== Definition Lists
CPU:: The brain of the computer.
Hard drive:: Permanent storage for operating system...
https://www.stoeps.de[] (1)
https://www.stoeps.de[Stoeps] (2)
1 | Anzeige des Links |
2 | Linktext wird angezeigt |
image::sunset.jpg[]
image::sunset.jpg[role=right] (1)
image::sunset.jpg[Image of a Sunset] (2)
image:sunset.jpg[] (3)
1 | CSS Rollen werden zum Element hinzugefügt |
2 | Setzen alt-Text |
3 | Inline Image |
<div class="imageblock"><img src="images/sunset.jpg" alt="sunset"></div>
<div class="imageblock right"><img src="images/sunset.jpg" alt="sunset"></div>
<div class="imageblock"><img src="images/sunset.jpg" alt="Image of a Sunset"></div>
<div class="paragraph"><p><span class="image"><img src="images/sunset.jpg" alt="sunset"></span></p></div>
[cols="1,1"]
|===
|Cell in column 1, row 1
|Cell in column 2, row 1
|Cell in column 1, row 2
|Cell in column 2, row 2
|Cell in column 1, row 3
|Cell in column 2, row 3
|===
[cols="1,1", options=header]
|===
|Cell in column 1, row 1
|Cell in column 2, row 1
|Cell in column 1, row 2
|Cell in column 2, row 2
|Cell in column 1, row 3
|Cell in column 2, row 3
|===
column 1, row 1 | column 2, row 1 |
column 1, row 2 | column 2, row 2 |
column 1, row 3 | column 2, row 3 |
column 1, row 1 | column 2, row 1 |
---|---|
column 1, row 2 | column 2, row 2 |
column 1, row 3 | column 2, row 3 |
Jedes Font Awesome Icon kann gesetzt werden
Muss im Dokument-Kopf aktiviert werden
:icons: font
icon:twitter[] Twitter https://twitter.com[@stoeps]
icon:linux[] Linux Icon
icon:windows[] Windows Icon
Admonition Blocks (Warning, Caution, Important, Note, Tip)
Font Awesome Icons mit :icons: font
WARNING: This is a warning
This is a warning |
CAUTION: This is a caution
This is a caution |
IMPORTANT: This is important
This is important |
NOTE: A note
TIP: Here is a tip
IMPORTANT: That's important
WARNING: Warning message
CAUTION: Caution, be careful
:experimental: (1)
1 | Die folgenden Beispiele sind noch als Experimental markiert |
.Copy Text
menu:Edit[Copy Special > Text]
.Button
Press kbd:[OK]
.Keyboard
kbd:[Ctrl+C] to stop this.
Adding [source]
.A Python function [source, python] ---- def function(): var x = 10 return x ----
Split longer documents and include Asciidoc Source
Include any type of files in [source]
Powerful
Include complete files
Include only parts
include::path/filename[] (1)
include::path/filename[lines=10..15] (2)
include::path/filename[tags=mytag] (3)
1 | Include entire file |
2 | Include lines 10-15 |
3 | Include area between mytag tags |
<html><head>
<title>example</title>
<!-- tag::stoeps[] -->
<!-- even comments -->
</head><body>
<h1>Test</h1>
<!-- end::stoeps[] -->
</body>
</html>
[source, indent=0]
----
include::test.html[tags=stoeps]
----
<!-- even comments -->
</head><body>
<h1>Test</h1>
[source]
----
def function:
x = 'secret' # <1>
print(secret)
return 0
----
<1> Hardcoded variable
def function:
x = 'secret' (1)
print(secret)
return 0
1 | Hardcoded variable |
= Connections Update bei Example Ltd (1)
:author: Christoph Stoettner (2)
:email: stoeps@vegardit.com
:lang: de (3)
:revnumber: 1.6 (4)
:revdate: 09.03.2024
:revremark: httpd.conf und TDI
:imagesdir: images (5)
:doctype: book
:source-highlighter: rouge (6)
:icons: font (7)
:sectnums: (8)
:sectnumlevels: 5
:toc: left (9)
:toclevels: 4
1 | Title |
2 | Autorenname und Mail |
3 | Sprache |
4 | Revision (Nummer, Datum, Info) |
5 | Verzeichnis für Bilder |
6 | Syntax Highlighter |
7 | Font Awesome für Icons |
8 | Nummerierung der Überschriften bis Level 5 |
9 | Inhaltsverzeichnis (bis Überschrift-Level 4) |
@startuml
skinparam rectangle {
BackgroundColor DarkSeaGreen
FontStyle Bold
FontColor DarkGreen
}
skinparam usecase {
BackgroundColor Pink
FontColor DarkRed
FontStyle Bold
}
skinparam note {
BackgroundColor LightYellow
FontColor Black
}
:user: as u
(Robot Framework) as rf
(PSTT) as pstt
(Check Library) as chk
rectangle network as n1{
}
rectangle network as n2{
}
note as tc
Test Case
==
Send...
OnRecv...
Get Data
Check
end note
note as script1
Script
==
Send...
Recv...
Get Data
end note
note as script2
Script
==
Send...
Recv...
Get Data
end note
note as report1
Report
==
Data1
Data2
Data...
end note
note as report2
Report
==
Data1
Data2
Data...
end note
note as checklist
Check List
==
Check1
Check2
end note
note as result
Result
==
Pass
Fail
Error
end note
u - tc
tc -> rf
rf . script1
script1 .> n1
n1 . script2
script2 .> pstt
pstt .. report1
n2 <. report1
report2 . n2
chk <. report2
rf -- checklist
checklist --> chk
result - chk
result --> u
@enduml
+-------------+
| |
| Exponential |
| |
+-------------+
|
lambda |
v
+-------------+ +-------------+ +---------+
| | tau | | lambda | |
| Lognormal |------->| Gamma |<----------| Poisson |
| | | |---+ | |
+-------------+ +-------------+ | +---------+
| ^ ^ | beta
| tau | | |
| tau | +---------+
| +-------------+
+--------------->| |
| Normal |
| |
+-------------+
gantt
title A Gantt Diagram
dateFormat YYYY-MM-DD
section Section
A task :a1, 2024-03-17, 30d
Another task :after a1, 20d
section Another
Task in Another :2024-03-20, 12d
another task :24d
flowchart LR
Start --> stop
costumes: {
shape: sql_table
id: int {constraint: primary_key}
silliness: int
monster: int
last_updated: timestamp
}
monsters: {
shape: sql_table
id: int {constraint: primary_key}
movie: string
weight: int
last_updated: timestamp
}
costumes.monster -> monsters.id
dogs -> cats -> mice: chase
replica 1 <-> replica 2
a -> b: To err is human, to moo bovine {
source-arrowhead: 1
target-arrowhead: * {
shape: diamond
}
}
Builtin
HTML
XHTML
DocBook
Man page
Add-ons
EPUB3
Reveal.js
Bespoke
Ruby Applikation
Es gibt aber auch
Java und Javascript Implementierungen
gem install asciidoctor
bundle init
echo "gem 'asciidoctor'" > Gemfile
bundle
Installation mit Linux Paketmanager
Debian / Ubuntu:
sudo apt-get install -y asciidoctor
RPM basiert:
sudo dnf install -y asciidoctor
Docker und Podman
Asciidoctor 2.0.21
Asciidoctor Diagram 2.3.0 with ERD and Graphviz integration (supports plantuml and graphiz diagrams)
Asciidoctor PDF 2.3.13
Asciidoctor EPUB3 2.1.0
Asciidoctor FB2 0.7.0
Asciidoctor Mathematical 0.3.5
Asciidoctor reveal.js 5.1.0
AsciiMath
Source highlighting using Rouge, CodeRay or Pygments
Asciidoctor Confluence 0.0.2
Asciidoctor Bibtex 0.9.0
Asciidoctor Kroki 0.9.1
Asciidoctor Reducer 1.0.2
example.adoc
= Chemnitzer Linux-Tage
Auch 2024 haben sich die Chemnitzer Linux-Tage einen Platz an einem März-Wochenende gesucht.
Also Kalender gezückt und den 16. und 17. März 2024 dick einkreisen! Es lohnt sich bestimmt.
https://chemnitzer.linux-tage.de/2024/de/info/eintritt/[Eintrittskarten] sind an der Tageskasse
erhältlich.
Wir freuen uns sehr, euch im März vor Ort in Chemnitz in gewohnter Umgebung wiederzusehen.
Über unsere Pressemitteilungen, Social Media könnt ihr euch diesbezüglich auf dem Laufenden halten.
image::images/martin-wettstein-4CVMWrWh3xU-unsplash.jpg[]
asciidoctor example.adoc
docker run --rm \
-u $(id -u):$(id -g) \
-v $(pwd):/documents/:Z \
asciidoctor/docker-asciidoctor \
asciidoctor example.adoc
podman run --rm \
-v /var/home/stoeps/devel/asciidoctor-presentations:/documents/:z \
docker.io/asciidoctor/docker-asciidoctor \
asciidoctor example.adoc
asciidoctor-pdf
erstellt eine PDF Version
Wir werden uns vor allem auf PDF Ausgaben konzentrieren
aber mit pandoc
kann aus den gegebenen Formaten praktisch jede Ausgabe erzeugt werden
pandoc
kann auch in Office Formate mit Vorlagen konvertieren
DocBook ist xml
und kann daher mittels xslt
oder xmlto weiterverarbeitet werden
Doc und Docx können zu Asciidoc konvertiert werden
pandoc input.docx -f docx -t asciidoc --wrap=none --markdown-headings=atx \
--extract-media=extracted-media -o output.adoc
asciidoctor-pdf documentation.adoc
asciidoctor-pdf -r asciidoctor-diagram documentation.adoc
podman run \
--rm \ (1)
-v /var/home/stoeps/devel/asciidoctor-presentations:/documents/:z \ (2)
-v /var/home/stoeps/.asciidoctor/theme:/theme/:Z \
docker.io/asciidoctor/docker-asciidoctor \
asciidoctor-pdf \
-a pdf-themesdir=/theme \ (3)
-a pdf-theme=stoeps-theme.yml \
example.adoc \
-o example-vegardit-book.pdf
1 | Container nach beenden löschen |
2 | Volume mounten (Dokumente mit Asciidoc und Theme für das Template) |
3 | -a Dokument-Attribute setzen (könnte auch im Source erfolgen) |
asciidoctor-pdf \
-r asciidoctor-diagram \ (1)
-a pdf-themesdir=~/.asciidoctor/theme \ (2)
-a pdf-theme=stoeps-theme.yml \ (3)
"document.adoc"
1 | Diagramme erstellen |
2 | PDF Template Verzeichnis |
3 | Template Name |
extends: default
base:
font-color: #FF0000
page:
layout: portrait
margin: 20mm
margin-inner: 25mm
margin-outer: 20mm
size: a4
base:
font-color: #333333
font-style: normal
font-size: 12
line_height_length: 14
line_height: $base_line_height_length / $base_font-size
title-page:
font-color: ffffff
background-color: #1d4e89
align: center
logo:
top: 10%
image: image:images/{logo-image-name}[width=30%, align=center] (1)
title:
top: 30%
font-size: $base_font-size * 4.25
font-style: bold
line-height: 0.9
subtitle:
font-size: $base_font-size * 2.00
line-height: 1
authors:
margin-top: $base_font-size * 29.25
font-size: $base_font-size * 1.5
revision:
margin-top: $base_font-size * 0.5
1 | {logo-image-name} ist eine Variable die im Asciidoc definiert wird |
header:
font-color: #c0c0c0
font-size: 10
border-color: #c0c0c0
border-width: 0.5
padding: [$base_line_height_length / 2, 1, 0, 1]
vertical-align: bottom
height: 1.5cm
recto: (1)
right:
content: '{section-or-chapter-title}'
left:
content: '{document-title}'
verso: (2)
right:
content: $header-recto-left-content
left:
content: $header-recto-right-content
1 | Ungerade Seiten |
2 | Gerade Seiten |
footer:
font-color: #c0c0c0
font-size: 10
border-color: #c0c0c0
border-width: 0.5
vertical-align: top
padding: [$base_line_height_length / 2, 1, 0, 1]
height: 1.5cm
recto:
right:
content: '{page-number}'
left:
content: '© {copyright-name}'
center:
content: 'Version {revnumber}, {revdate}'
verso:
left:
content: $footer-recto-right-content
right:
content: $footer-recto-left-content
center:
content: $footer-recto-center-content
= Chemnitzer Linux-Tage
:author: Christoph Stoettner
:email: stoeps@vegardit.com
:doctype: book
:logo-image-name: vegardit-logo.png
Templates in rollen-name/templates
*.j2
vim
Syntax highlighting kann z.B. mit Kommentar in Kopf- oder Fusszeile forciert werden
// vim: syntax=asciidoc
yaml
Definitionvariable: Hallo
users:
- root
- stoeps
- ansible
# alternative Schreibweise
users: ['root', 'stoeps', 'ansible']
stoeps:
name: Christoph Stoettner
mail: stoeps@vegardit.com
group: wheel
# alternative Schreibweise
stoeps: {name: Christoph Stoettner, mail: stoeps@vegardit.com, group: wheel}
users:
- stoeps:
name: Christoph Stoettner
mail: stoeps@vegardit.com
group: wheel
- ansible:
name: Ansible User
mail: ansible@example-domain.home
group: ansible
Variablen sind in environments/prod/group_vars/all/
definiert
aber nur die vom Default der Rollen abweichen
D.h. wir können aus einer Rolle Dokumentation nicht auf die Default-Variablen anderen Rollen zugreifen
Modul: ansible.builtin.include_vars
Kann yaml
und json
Variablen Definitionen einlesen
Rekursiv in Verzeichnissen möglich
vars
- name: Include variables from other roles
ansible.builtin.include_vars:
file: '{{ item }}'
with_items:
- ../../../roles/hcl/connections/vars/main.yml
- ../../../roles/third_party/ibm/wasnd/was-dmgr-full-sync-nodes/vars/main.yml
- ../../../roles/third_party/ibm/wasnd/was-dmgr-config-ldap/vars/main.yml
- ../../../roles/third_party/ibm/db2-install/vars/main.yml
- ../../../roles/third_party/ibm/ihs/ibm-http-config-plgwct/vars/main.yml
Template-Engine für Python
Platzhalter werden mit dynamischen Inhalten ersetzt
Unterstützt Schleifen, Bedingungen, Variablen, Filter und Funktionen
Einfügen von Variablen und Ausdrücken
{{ … }}
Kommentare
{# … #}
Mehr Lesbarkeit des Codes
my_var: Hallo
{{ my_var }} Christoph
Hallo Christoph
{#
The code is
documentation enough
#}
Jinja2 Code (Statements, Control structures)
{% … %}
for
-Schleifen{% for user in users %}
{{ users[user].mail }}
{% else %}
Kein User vorhanden!
{% endfor %}
if
-Blöcke{% if users|length > 1 %}
Viele User!
{% elif users|length == 1 %}
Schon ein User!
{% else %}
Bisher keine User!
{% endif %}
---
# defaults file for clt-jinja2
users:
stoeps:
name: Christoph Stoettner
mail: stoeps@vegardit.com
group: wheel
ansible:
name: Ansible User
mail: ansible@example-domain.home
group: ansible
for
-Schleifestoeps@vegardit.com
ansible@example-domain.home
if
-BlockViele User!
- name: Generate Asciidoc documentation from Jinja2 template
ansible.builtin.template: (1)
src: documentation.adoc.j2 (2)
dest: /tmp/documentation.adoc (3)
# owner: root
# group: root
# mode: '0600'
# validate: /usr/sbin/sshd -t -f %s
# backup: yes
1 | Ansible module |
2 | Name des Quell-Dokuments (im Template Ordner der Rolle) |
3 | Name des Ziel-Dokuments (Jinja2 Variablen ersetzt) |
Es macht keinen grossen Unterschied
Um Teile von Source Code in die Dokumentation aufzunehmen, nehme ich Asciidoctor include
Variablen und Conditionals mache ich normalerweise im Jinja2
$ ansible-galaxy init clt-documentation
$ tree clt-documentation
clt-documentation
├── defaults
│ └── main.yml
├── files
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── README.md
├── tasks
│ └── main.yml
├── templates
├── tests
│ ├── inventory
│ └── test.yml
└── vars
└── main.yml
defaults/main.yml
- Asciidoctor DefaultsDefinition von Default-Variablen und Option aus group_vars zu lesen
__migration_title: '{{ migration_title | default("Connections Update") }}'
__author_name: '{{ author_name | default("Joe Doe") }}'
__author_email: '{{ author_email | default("joe.doe@vegardit.com") }}'
__author_company: '{{ author_company | default("My Company Ltd") }}'
__customer: '{{ customer | default("Example Ltd.") }}'
__revdate: "{{ revdate | default(now(utc=true,fmt='%d.%m.%Y')) }}"
__revnumber: "{{ revision | default('1.0') }}"
__revhistory: "{{ revhistory }}"
__revremark: "{{ revremark | default('Describe latest change') }}"
__src_highlighter: "{{ src_highlighter | default('rouge') }}"
defaults/main.yml
- Sammeln von Variablen aus anderen RollenVariablen die nicht direkt aus den anderen Rollen gefüllt werden können, kann man hier aufbereiten
Die hier verwendeten Variablen kommen aus den eingelesenen vars/main.yaml
Dateien
__credentials:
user1:
function: database user
name: lcuser
password: "{{ __db2_users_password }}"
user2:
function: database user
name: docsuser
password: "{{ __db2_users_password }}"
user3:
function: DMGR Admin
name: "{{ __was_username }}"
password: "{{ __was_password }}"
user4:
function: HTTP Admin
name: "{{ __ihs_username }}"
password: "{{ ihs_password }}"
user5:
function: LDAP Bind
name: "{{ __ldap_bind_user }}"
password: "{{ __ldap_bind_pass }}"
- name: Create folder with documentation
ansible.builtin.file:
path: '{{ ansible_env.HOME }}/doc-temp'
state: directory
Man kann noch abfangen was passieren soll, wenn der Ordner besteht
Temp Ordner mit random Name wäre eine Option
Backup vorhandener Dateien erstellen?
Nach Möglichkeit die Module command
und shell
vermeiden
community.docker.docker_container
tasks/main.yml
- name: Generate with docker_container
community.docker.docker_container:
name: asciidoctor (1)
image: asciidoctor/docker-asciidoctor (2)
command: asciidoctor-pdf documentation.adoc -o /documents/container-doc.pdf (3)
auto_remove: true (4)
volumes:
- '/{{ ansible_env.HOME }}/doc-temp:/documents:z' (5)
1 | Name des Containers (nicht relevant, siehe 4) |
2 | Image des Containers |
3 | Ausgeführtes Kommando |
4 | Container nach Ausführung löschen |
5 | Volumes mount |
- name: Generate with podman container
containers.podman.podman_container:
name: asciidoctor
image: docker.io/asciidoctor/docker-asciidoctor
command: asciidoctor-pdf documentation.adoc -o /documents/podman-doc.pdf
auto_remove: true
volumes:
- '/{{ ansible_env.HOME }}/doc-temp:/documents:z'
Dokumentkopf
Tabellen
Listen