{"id":379,"date":"2025-04-16T12:00:41","date_gmt":"2025-04-16T10:00:41","guid":{"rendered":"https:\/\/www.florian-klaus.de\/?p=379"},"modified":"2025-04-16T12:09:18","modified_gmt":"2025-04-16T10:09:18","slug":"umzug-des-servers-von-ubuntu-auf-debian","status":"publish","type":"post","link":"https:\/\/www.florian-klaus.de\/index.php\/2025\/04\/16\/umzug-des-servers-von-ubuntu-auf-debian\/","title":{"rendered":"Umzug des Servers von Ubuntu auf Debian"},"content":{"rendered":"\n<p>Der Neuaufbau eines Servers erfordert immer Planung. Gibt es \u00c4nderungen in der bereitgestellten Hardware, gibt es neue und vor allem bessere Software zum Erreichen der Ziele? <\/p>\n\n\n\n<p>Aber zun\u00e4chst gilt es die Daten, welche anschlie\u00dfend noch ben\u00f6tigt werden, zu sichern. Das hei\u00dft, das Backup sollte vollumf\u00e4nglich sein. In meinem Fall war dies zun\u00e4chst die Sicherung der virtuellen Maschinen, und da sich unter den VM auch ein NAS und eine Cloud war, auch eine zus\u00e4tzliche Sicherung der Daten. <\/p>\n\n\n\n<p>Die vollzieht man vor der Sicherung der VMs. Allerdings gestaltet sich die Sicherung der VMs bei dem Volumen an VMs, welches ich laufen hatte, als schwierig. Jede einzelne VM zu sichern, w\u00fcrde immense Zeit in Anspruch nehmen. Also w\u00e4re ein Script die Wahl.<\/p>\n\n\n\n<p>Das Ganze wird nat\u00fcrlich auf einem Backup-Server gesichert. Als Grundlage habe ich mir einen etwas in die Jahre gekommenen Athlon 5350 genommen, ihn mit einer kleinen SSD und einer gro\u00dfen Festplatte best\u00fcckt. Die CPU ist schwachbr\u00fcstig, aber das macht ehrlich gesagt nichts, denn mehr als das OS hosten und die Daten f\u00fcr die Backup-Platte entgegennehmen muss sie nicht.<\/p>\n\n\n\n<p>Dennoch w\u00e4re es sch\u00f6n, wenn der Backup-Server nur dann laufen muss, wenn er wirklich ben\u00f6tigt wird. Und wozu gibt es WakeOnLAN, wenn nicht f\u00fcr solche Situationen?<\/p>\n\n\n\n<p>Also kurzerhand die notwendigen Einstellungen im Bios gesetzt, und dann versucht, ihn mit Netplan zum Annehmen von Paketen zu bewegen.<\/p>\n\n\n\n<p>Den zun\u00e4chst testet man erstmal, ob das System WakeOnLan Pakete entgegennimmt.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo ethtool eth0 | grep Wake-on\n        Supports Wake-on: pumbg\n\tWake-on: g<\/code><\/pre>\n\n\n\n<p>Das w\u00e4re die optimale Einstellung, allerdings war dies bei mir nicht der Fall. Bei mir sagte der Server, dass er keine WakeOnLAN Pakete entgegennahm.<\/p>\n\n\n\n<p>Es gibt im Netplan die Option:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>wakeonlan: true<\/code><\/pre>\n\n\n\n<p>Aber die brachte mich auch nicht weiter.<\/p>\n\n\n\n<p>Was mir dann doch geholfen hat, war ein Service zu kreieren, der mir mithilfe von ethtool den Service startet:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;Unit]\nDescription=Wake-on-LAN for eth0\nRequires=network.target\nAfter=network.target\n\n&#91;Service]\nExecStart=\/sbin\/ethtool -s eth0 wol g\nType=oneshot\n\n&#91;Install]\nWantedBy=multi-user.target<\/code><\/pre>\n\n\n\n<p>Dieses Codesnippet in \/etc\/systemd\/system als wol@eth0.service gespeichert und dann gestartet, und siehe da, der PC nimmt jetzt WakeOnLan Pakete an.<\/p>\n\n\n\n<p>Zur weiteren Vorbereitung galt es dann, sich passwortlos auf dem Backup-Server anmelden zu k\u00f6nnen, am besten via ssh.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ssh-keygen -t ed25519 -f \/Pfad\/zum\/privatenSchl\u00fcssel -C \"Kommentar zum Schl\u00fcssel\"<\/code><\/pre>\n\n\n\n<p>Dieser Befehl erstellt ein Paar Schl\u00fcssel. Einen privaten, und einen \u00f6ffentlichen, welcher die Endung  .pub (public) erh\u00e4lt.  Bei der Erstellung wird eine doppelte Passwortabfrage zum privaten Schl\u00fcssel kommen, diese sollte mit einem ausreichenden Passwort beantwortet werden, kann aber auch leer gelassen werden. Ascnhlie\u00dfend transferiert man den \u00f6ffentlichen Schl\u00fcssel auf die gwe\u00fcnschte Maschine.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ssh-copy-id -i \/Pfad\/zum\/\u00f6ffentlichenSchl\u00fcssel.pub benutzername@&lt;IP Adresses oder Hostname><\/code><\/pre>\n\n\n\n<p>Somit kann man jetzt: Anschalten, einloggen, Schei\u00dfe bauen, aber es fehlt noch: <strong>Ausschalten<\/strong>.<\/p>\n\n\n\n<p>Dies geht zwar auch via SSH, aber sp\u00e4testens bei dem Befehl:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo shutdown now<\/code><\/pre>\n\n\n\n<p>verlangt das System dann das Passwort.<\/p>\n\n\n\n<p>Aber auch da gibt es Abhilfe:<\/p>\n\n\n\n<p>Entweder man kreiert eine Gruppe, f\u00fcgt den Nutzer hinzu, der passwortlos den Shutdown einleiten kann, oder man l\u00e4sst es den User direkt machen.<\/p>\n\n\n\n<p>Sollte man die Gruppe anlegen wollen, muss man \/etc\/group editieren.<\/p>\n\n\n\n<p>Auf jeden Fall muss man anschlie\u00dfend \/etc\/sudoers editieren:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>%Groupname ALL=NOPASSWD: \/sbin\/shutdown\n\nUsername ALL=NOPASSWD: \/sbin\/shutdown<\/code><\/pre>\n\n\n\n<p>Der erste Befehl erlaubt der Gruppe den passwortlosen shutdown, der zweite dem User. Es sollte selbstverst\u00e4ndlich sein, dass der Nutzer, mit dem der ssh Zugriff erfolgt, der Gruppe angeh\u00f6ren sollte, oder aber der singul\u00e4re Nutzer ist, der das System passwortlos herunterfahren darf.<\/p>\n\n\n\n<p>Nachdem jetzt die Vorbereitungen getroffen sind, das Script:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/bin\/bash\n###################################################################################################\n#                                                                                                 #\n#                                                                                                 #\n#                          Backup of Virtual Machines Script                                      #\n#                                                                                                 #\n#                                                                                                 #\n#                                                                                                 #\n#                                  by Florian Klaus                                               #\n#                                                                                                 #\n#                                                                                                 #\n#                                                                                                 #\n###################################################################################################\n\n# We need xmlllint and wakeonlan, let's scheck if the correspondings software pakets are installed\n# xmllint is part of libxml2-utils, etherwake will install wakeonlan automaticly and is also needed\npaketEtherwake=$(dpkg -s etherwake | grep Status)\npaketLibxmlutils=$(dpkg -s libxml2-utils | grep Status)\n\n#echo $paketEtherwake\n#echo $paketLibxmlutils\n\nif &#91;&#91; $paketEtherwake = \"Status: install ok installed\" ]] &amp;&amp; &#91;&#91; $paketLibxmlutils = \"Status: install ok installed\" ]]\nthen\n\techo \"etherwake and libxml2-utils are installed\"\nelif &#91;&#91; $paketEtherwake = \"Status: install ok installed\" ]] &amp;&amp; &#91;&#91; $paketLibxmlutils != \"Status: install ok installed\" ]]\nthen\n\techo \"libxml2-utils musst be installed, doing so now\"\n\tapt update\n\tapt install -y libxml2-utils\nelif &#91;&#91; $paketEtherwake != \"Status: install ok installed\" ]] &amp;&amp; &#91;&#91; $paketLibxmlutils = \"Status: install ok installed\" ]]\nthen\n\techo \"etherwake musst be installed. doing so now\"\n\tapt update\n\tapt install -y etherwake\nelif &#91;&#91; $paketEtherwake != \"Status: install ok installed\" ]] &amp;&amp; &#91;&#91; $paketLibxmlutils != \"Status: install ok installed\" ]]\nthen\n\techo \"neither Paket is installed, installing both now\"\n\tapt update\n\tapt install -y etherwake libxml2-utils\nfi\n\n# Highest Number of source files in a single (!!!) VM installation. Source files may include the installation disk, \n# that is still in use, for one reason or another (mostly .iso-files), but they may also include different hard disks \n# associated with on VM (qcow2-files). \n# Check your installations and take the highest number. if it exceeds 5, then, first of all,\n# may god have mercy on your soul. But you will have to set this number higher. Default is 3.\n\nsourceFileHigh=3\n\n\n# Excluded VMs - Virtual Machines you might want to exclude (for reasons) from the backup\n# We need the name of the VM, as defined in the corresponding XML File\n# Since this file is going to be parsed one line at a time, one VM per line please\n\nexcludedVMsFile=\"excludedVMs.txt\"\n\nnrOfExcludedVMs=$(wc -l &lt; $excludedVMsFile)\n\n\n# the stat command-output is localized, so please check a file e.g. stat filename.txt and look at the last 4 lines, \n# and remember, they are case-sensitive!!!:\n\n# lastModifiedFile=\"Modify\"\n# lastChangedFile=\"Change\"\n# birthOfFile=\"Birth\"\nlastModifiedFile=\"Modifiziert\"\nlastChangedFile=\"Ge\u00e4ndert\"\nbirthOfFile=\"Geburt\"\n\n\n# XML Folder, where the XMLs for the VirtualMachines are stored, usually '\/etc\/libvirt\/qemu'\n\nxmlDir=\"\/etc\/libvirt\/qemu\/\"\n\n\n# temporal directory to save backup files in. \/tmp is not chosen because it only lasts until reboot\n\nlocalBackupDir=\/var\/tmp\/backup-vms\n\n#local sshfs mount directory, needs to be created\n\nlocalMntDir=\/mnt\/backup-server\n\nif &#91;&#91; -d $localMntDir ]]\nthen\n\techo \"Local mount directory exists, nothing to do here ...\"\nelse\n\techo \"Local mount directory doesn't exist, creating ...\"\n\tmkdir $localMntDir\nfi\n\n# external Data\n\nexternalIP=\"192.168.168.192\"\n\nmacAddress=\"FF:FF:FF:FF:FF:FF\"\n\nexternalUserName=\"Username\"\n\nexternalBackupDir=\"\/home\/$externalUserName\/vm-backup-dir\"\n\n# Establish connection to backup-server\n\nping -c 1 $externalIP  &amp;> \/dev\/null\npingStatus1=$( echo $? )\nif &#91;&#91; $pingStatus1 == 0 ]]\nthen\n\techo \"Backup-Server online, establishing connection\"\n\tmountPoint=\"$externalUserName@$externalIP:$externalBackupDir $localMntDir\"\n\tsshfs $mountPoint\n\tsleep 5s\n\t#exit\nelse\n\techo \"Backup-Server offline, trying to start it now ...\"\n\twakeonlan $macAddress\n\tsleep 120s\n\tping -c 1 $externalIP &amp;> \/dev\/null\n\tpingStatus2=$( echo $? )\n\tif &#91;&#91; $pingStatus2 == 0 ]]\n\tthen\n\t\techo \"Backup-Server woken up, establishing connection\"\n\t\tmountPoint=\"$externalUserName@$externalIP:$externalBackupDir $localMntDir\"\n\t\tsshfs $mountPoint\n\t\tsleep 5s\n\t\t#exit\n\telse\n\t\techo \"No Connection to the back-server, exiting ...\"\n\t\texit\n\tfi\nfi\n\n# Let's check if the local backup directory excists, and, if not, create it\n\nif &#91;&#91; -d \"$localBackupDir\" ]]\nthen\n\techo \"local backup directory exists, nothing to do here ...\"\nelse\n\techo \"local backup directory doesn't exist, creating ...\"\n\tmkdir \"$localBackupDir\"\nfi\n\n# Let's check if the Exclusion File is found\n\nif &#91; -f $excludedVMsFile ]\nthen\n\techo \"found exclusion file\"\nfi\n\n# Let's name our VM textfile\nvmTextFile=\"listOfXMLs.txt\"\n\n# Remove any list of XMLs, if there is any\n\nif &#91; -f $vmTextFile ]\nthen\n\techo \"$vmTextFile exists, removing ...\"\n\trm $vmTextFile\nfi\n\n\n# Find all XMLs in the given XMLDirectory, and make a List of them\nfind $xmlDir*.xml | rev | cut -d '\/' -f1 | rev > $vmTextFile\n\n# count the Number of XMLs, e.g. the Number of virtual machines\n\nnrOfXMLs=$(wc -l &lt; $vmTextFile)\n\n# Let's create a file with the current running VMs\n\nrunningVMs=\"runningVMs.txt\"\n\n# remove any list of running VMs, if there is any\n\nif &#91; -f $runningVMs ]\nthen\n\techo \"$runningVMs exists, removing ...\"\n\trm $runningVMs\nfi  \n\nps -ef | grep qemu-system-x86_64 | cut -d',' -f1 | grep name | cut -d'=' -f2 > \"$runningVMs\"\n\n# Let's see how many running VMs we have\n\nnrOfRunningVMs=$(ps -ef | grep qemu-system-x86_64 | cut -d',' -f1 | grep name | cut -d'=' -f2 | wc -l)\n\n# Now we iterate through the list of XMLs (e.g. VMs) and start doing shit with them \n\nfor (( i=1 ; i&lt;=$nrOfXMLs ; i++ ))\ndo \n\t# the current VM\n\tcurrentXML=$(sed \"${i}q;d\" $vmTextFile)\n\t\n\t# We grab the Name of the current VM, and cut loose all the shit that thing doesn't need.\n\tnameOfVM=$(xmllint \"$xmlDir$currentXML\" | grep '&lt;name>' | cut -d '>' -f2 | rev | cut -d '&lt;' -f2 | rev)\n\n\t# Check if the VM to backup isn't exscluded.\n\tif &#91; -f $excludedVMsFile ]\n\tthen\n\t\tfor (( n = 1; n &lt;= $nrOfExcludedVMs; n++ )) \n\t\tdo\n\t\t\texcludedVM=$(sed \"${n}q;d\" $excludedVMsFile)\n\t\t\tif &#91;&#91; $nameOfVM = $excludedVM ]]\n\t\t\tthen\n\t\t\t\techo \"Nothing to do with $nameOfVM\"\n\t\t\t\tbreak\n\t\t\tfi\n\t\tdone\n\telse\n\t\techo \"$nameOfVM is not excluded, continuing ...\"\n\tfi\n\tif &#91;&#91; $nameOfVM = $excludedVM ]]\n\tthen\n\t\techo \"$nameOfVM has been excluded\"\n\t\tcontinue\n\telif &#91;&#91; $nameOfVM != $excludedVM ]]\n\tthen\n\t\tif &#91;&#91; $nrOfRunningVMs -ge 1 ]]\n\t\tthen \n\t\t\tfor (( x = 1; x &lt;= $nrOfRunningVMs; x++ ))\n\t\t\tdo \n\t\t\t\tcurrentRunningVM=$(sed \"${x}q;d\" $runningVMs)\n\t\t\t\tif &#91;&#91; $nameOfVM = $currentRunningVM ]]\n\t\t\t\tthen\n\t\t\t\t\techo \"$nameOfVM is currently running, suspending ...\"\n\t\t\t\t\tvirsh suspend $nameOfVM\n\t\t\t\t\tbreak\n\t\t\t\tfi\n\t\t\tdone\n\t\tfi\n\n\t\t# bash in itself doesn't let one operate with objects, so we are circumventing this \n\t\t# right now by creating txt-based backupfiles\n\t\n\t\t# We create a current backupfile\n\t\tcurrentBackupFile=\"$nameOfVM\"-Backup.txt\"\"\n\n\t\tcurrentFileSourceFile=\"$nameOfVM\"-SourceFile.txt\"\"\n\t\n\t\t# The current Backupfile is obsolete, so we are deleting it\n\t\tif &#91;&#91; -f $currentBackupFile ]]\n\t\tthen \n\t\t\trm \"$currentBackupFile\"\n\t\tfi\n\t\n\t\t# The name of the VM is the first line in the newly created backup file\n\t\techo \"$nameOfVM\" > \"$currentBackupFile\"\n\t\n\t\t# The XML file (including the directory path) is the second line in the backup file\n\t\techo \"\"XML File=\"$xmlDir$currentXML\" >> \"$currentBackupFile\"\n\t\n\t\t# Determining how many source files are there, e.g. we count the number of sorce files. Source files may include the \n\t\t# installation disk (mostly .iso-files),\n\t\t# but they may also include different hard disks (qcow2-files). That's why we set the number earlier\n\t\t\n\t\tcurrentNrOfSourceFiles=$(xmllint \"$xmlDir$currentXML\" | grep 'source file' | wc -l)\n\t\t\n\t\tcurrentSourceFiles=$(xmllint \"$xmlDir$currentXML\" | grep 'source file')\n\n\t\tif &#91;&#91; -f $currentFileSourceFile ]]\n\t\tthen\n\t\t\trm \"$currentFileSourceFile\"\n\t\tfi\n\n\t\t# declare k and l, which will incrememnted by 2 every time in the loop, to cut out the path for the different source files\n\t\tk=1\n\t\tl=2\t\n\n\t\t# Now comes the other for loop, in which we will itereate through the different source files. See above\n\n\t\tfor (( j=1 ; j&lt;=$sourceFileHigh ; j++  ))\n\t\tdo\t\t\t\n\n\t\t\tcutSourceFile=$(echo $currentSourceFiles | grep 'source file' | cut -d \" \" -f${k}-${l}) \n\t\t\tmodSourceFile=$(echo $cutSourceFile | cut -d \"=\" -f2 | cut -d '\"' -f2) #| cut -d \">\" -f1 | rev | cut -d \"\/\" -f2 | rev | cut -d '\"' -f1)\n\t\t\t\n\t\t\t# if the source file is empty, just put a blank line in there\n\t\t\tif &#91;&#91; -z $modSourceFile ]]\n\t\t\tthen\n\t\t\t\techo \"$modSourceFile\" >> \"$currentBackupFile\"\n\t\t\t# else put the following info in there\n\t\t\telse\n\t\t\t\techo \"\"Source Path=\"$modSourceFile\" >> \"$currentBackupFile\"\n\n\t\t\t\techo \"$modSourceFile\" >> \"$currentFileSourceFile\"\n\n\t\t\t\t# now, let's dive into the statistics for the source file \n\n\t\t\t\t# now, let's grep the info and store it in a file\n\t\t\t\tmodifySourceFile=$(stat $modSourceFile |  grep $lastModifiedFile | cut -d \":\" -f2-4 | cut -d \" \" -f2-4)\n\t\t\t\techo \"\"Last Modified=\"$modifySourceFile\" >> \"$currentBackupFile\"\n\t\t\t\tchangeSourceFile=$(stat $modSourceFile |  grep $lastChangedFile | cut -d \":\" -f2-4 | cut -d \" \" -f2-4)\n\t\t\t\techo \"\"Last Changed=\"$changeSourceFile\" >> \"$currentBackupFile\"\t\t\t\n\t\t\t\tbirthOfSourceFile=$(stat $modSourceFile |  grep $birthOfFile | cut -d \":\" -f2-4 | cut -d \" \" -f2-4)\n\t\t\t\techo \"\"Birth Of File=\"$birthOfSourceFile\" >> \"$currentBackupFile\"\n\t\t\t\n\t\t\t\t\n\n\t\t\tfi\n\t\n\n\t\t\t# k needs to be incremented by 2\n\t\t\tk=$((k+2))\n\n\t\t\t# l needs to be incremented by 2\n\t\t\tl=$((l+2))\n\t\t\n\t\tdone\n\n\t\t# Last line in file\n\t\techo \"End of File\" >> \"$currentBackupFile\"\n\n\n\t\t# Check if the local backup directory exists, and, if it doesn't, create it\n\t\tif &#91;&#91; -d \"$localBackupDir\/$nameOfVM\" ]]\n\t\tthen\n\t\t\techo \"local backup directory for $nameOfVM exists, nothing to do here ...\"\n\t\telse\n\t\t\techo \"local backup directory for $nameOfVM doesn't exist, creating ...\"\n\t\t\tmkdir \"$localBackupDir\/$nameOfVM\"\n\t\tfi\n\n\t\t# Let's check if there is a backup file already and compare it to the current file.\n\t\t# Since the we just accumulated all the information about the VM, we check if the VM has been started in any way, \n\t\t# and if it hasn't, we won't need to do anything.\n\t\t# In the aforementioned case, the loop will stop here and continue to the next VM\n\t\t\n\t\tif &#91;&#91; -f \"$localBackupDir\/$nameOfVM\/$currentBackupFile\" ]]\t\t\n\t\tthen\n\t\t\tif &#91;&#91; 'cmp \"$localBackupDir\/$nameOfVM\/$currentBackupFile\" $currentBackupFile' ]]\n\t\t\tthen\n\t\t\t\techo \"The VM hasn't changed since the last backup, no need to do anything ...\"\n\t\t\t\trm \"$currentBackupFile\"\n\t\t\t\trm \"$currentFileSourceFile\"\n\t\t\t\tcontinue\n\t\t\telse\n\t\t\t\techo \"The VM has changed, removing old backup file ...\"\n\t\t\t\trm \"$localBackupDir\/$nameOfVM\/$currentBackupFile\"\n\t\t\t\techo \"Writing new local backup file\"\n\t\t\t\trsync --progress \"$currentBackupFile\" \"$localBackupDir\/$nameOfVM\/$currentBackupFile\"\n\t\t\tfi\n\t\telse\n\t\t\techo \"The backup file doesn't exist, creating ...\"\n\t\t\trsync --progress \"$currentBackupFile\" \"$localBackupDir\/$nameOfVM\/$currentBackupFile\"\n\t\tfi\n\n\t\tif &#91;&#91; -d \"$localMntDir\/$nameOfVM\" ]]\n\t\t\tthen\n\t\t\t\techo \"Directory for $nameOfVM exists, nothing to do here ...\"\n\t\t\telse\n\t\t\t\techo \"Directory for $nameOfVM doesn't exist, creating ...\"\n\t\t\t\tmkdir \"$localMntDir\/$nameOfVM\"\n\t\t\tfi\n\n\t\tdate=\"$(date '+%Y-%m-%d')\"\n\n\t\t# This checks if the backup was done today, and if it was, it assumes that something went wrong \n\t\t# (usually there are not two backups in a single day,)\n\t\t# If this is the second backup in one day, it removes everything.\n\t\t\t\n\t\tif &#91;&#91; -d \"$localMntDir\/$nameOfVM\/$date\" ]]\n\t\tthen\n\t\t\techo \"Todays date directory exists, cleaning it up ...\"\n\t\t\tif &#91;&#91; -f \"$localMntDir\/$nameOfVM\/$date\/$currentXML\" ]]\n\t\t\tthen\n\t\t\t\techo \"$currentXML exists, removing ...\"\n\t\t\t\trm -v \"$localMntDir\/$nameOfVM\/$date\/$currentXML\"\n\t\t\tfi\n\t\t\tif &#91;&#91; -f \"$localMntDir\/$nameOfVM\/$date\/$currentBackupFile\" ]]\n\t\t\tthen\n\t\t\t\techo \"$currentBackupFile exists, removing ...\"\n\t\t\t\trm -v \"$localMntDir\/$nameOfVM\/$date\/$currentBackupFile\"\n\t\t\tfi\n\t\telse\n\t\t\techo \"Todays date directory doesn't exist, creating ...\"\n\t\t\tmkdir \"$localMntDir\/$nameOfVM\/$date\"\n\t\tfi\n\n\t\t# Last but not least, the copying process of the needed files\n\n\t\t\tif &#91;&#91; -d \"$localMntDir\/$nameOfVM\/$date\" ]]\n\t\t\tthen\n\t\t\t\techo \" Copying text files to backup destination ...\"\n\t\t\t\trsync --progress \"$xmlDir$currentXML\" \"$localMntDir\/$nameOfVM\/$date\/\"\n\t\t\t\trsync --progress \"$currentBackupFile\" \"$localMntDir\/$nameOfVM\/$date\/\"\n\t\t\tfi\n\n\t\tsleep 10s\n\n\t\tnrOfSourceFiles=$(wc -l &lt; $currentFileSourceFile)\n\n\t\tfor (( z = 1; z &lt;= $nrOfSourceFiles; z++ ))\n\t\tdo\n\t\t\tcurrentQcow2=$(sed -n \"${z}p\" \"$currentFileSourceFile\")\n\t\t\tcurrentQcow2File=$(sed -n \"${z}p\" \"$currentFileSourceFile\" | rev | cut -d '\/' -f1 | rev)\n\n\t\t\tif &#91;&#91; -f \"$localMntDir\/$nameOfVM\/$date\/$currentQcow2File\" ]]\n\t\t\tthen\n\t\t\t\techo \"$currentQcow2 exists, removing ...\"\n\t\t\t\trm -v \"$localMntDir\/$nameOfVM\/$date\/$currentQcow2File\"\t\n\t\t\tfi\n\n\t\t\tif &#91;&#91; -d \"$localMntDir\/$nameOfVM\/$date\" ]]\n\t\t\tthen\n\t\t\t\techo \" Copying $currentQcow2 files to backup destination ...\"\n\t\t\t\trsync --progress \"$currentQcow2\" \"$localMntDir\/$nameOfVM\/$date\/\"\n\t\t\tfi\n\t\t\t\n\t\tdone\n\t\t\n\t\t# Cleanup ...\n\n\t\techo \"removing created files ...\"\n\n\t\trm \"$currentBackupFile\"\n\t\trm \"$currentFileSourceFile\"\n\n\t\t# We need to resume the VM, if it was running in the first place ...\n\n\t\tif &#91;&#91; $nameOfVM = $currentRunningVM ]]\n\t\tthen\n\t\t\techo \"$nameOfVM needs to be resumed!\"\n\t\t\tvirsh resume $nameOfVM\n\t\tfi\n\tfi\ndone\n\n# Cleanup ...\n\nrm \"$vmTextFile\"\nrm \"$runningVMs\"\n\n# Unmount the sshfs directory\n\nfusermount -u $localMntDir\n\n# shutdown the remote backup-server\n\nssh \"$externalUserName@$externalIP\" \"sudo shutdown now\"<\/code><\/pre>\n\n\n\n<p>Knapp 450 geschmeidige Zeilen. Aber warum so viel?<\/p>\n\n\n\n<p>Das Unterfangen w\u00e4re mit python wahrscheinlich einfacher gewesen, aber ich wollte es komplett auf bash-ebene durchziehen. Dennoch werden zwei Pakete ben\u00f6tigt, und die ersten Zeilen \u00fcberpfr\u00fcfen ob diese installiert sind und tuen dies, wenn sie es nicht sind.<\/p>\n\n\n\n<p>Anschlie\u00dfend werden ein paar Abfragen gestellt:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Highest Number of source files in a single (!!!) VM installation. Source files may include the installation disk, \n# that is still in use, for one reason or another (mostly .iso-files), but they may also include different hard disks \n# associated with on VM (qcow2-files). \n# Check your installations and take the highest number. if it exceeds 5, then, first of all,\n# may god have mercy on your soul. But you will have to set this number higher. Default is 3.\n\nsourceFileHigh=3\n\n\n# Excluded VMs - Virtual Machines you might want to exclude (for reasons) from the backup\n# We need the name of the VM, as defined in the corresponding XML File\n# Since this file is going to be parsed one line at a time, one VM per line please\n\nexcludedVMsFile=\"excludedVMs.txt\"\n\nnrOfExcludedVMs=$(wc -l &lt; $excludedVMsFile)\n\n\n# the stat command-output is localized, so please check a file e.g. stat filename.txt and look at the last 4 lines, \n# and remember, they are case-sensitive!!!:\n\n# lastModifiedFile=\"Modify\"\n# lastChangedFile=\"Change\"\n# birthOfFile=\"Birth\"\nlastModifiedFile=\"Modifiziert\"\nlastChangedFile=\"Ge\u00e4ndert\"\nbirthOfFile=\"Geburt\"\n\n\n# XML Folder, where the XMLs for the VirtualMachines are stored, usually '\/etc\/libvirt\/qemu'\n\nxmlDir=\"\/etc\/libvirt\/qemu\/\"\n\n\n# temporal directory to save backup files in. \/tmp is not chosen because it only lasts until reboot\n\nlocalBackupDir=\/var\/tmp\/backup-vms\n\n#local sshfs mount directory, needs to be created\n\nlocalMntDir=\/mnt\/backup-server\n\nif &#91;&#91; -d $localMntDir ]]\nthen\n\techo \"Local mount directory exists, nothing to do here ...\"\nelse\n\techo \"Local mount directory doesn't exist, creating ...\"\n\tmkdir $localMntDir\nfi\n\n# external Data\n\nexternalIP=\"192.168.168.192\"\n\nmacAddress=\"FF:FF:FF:FF:FF:FF\"\n\nexternalUserName=\"Username\"\n\nexternalBackupDir=\"\/home\/$externalUserName\/vm-backup-dir\"<\/code><\/pre>\n\n\n\n<p>Zun\u00e4chst einmal wird die maximale Anzahl an Source Files abgefragt. Dies sollten Platten sein, die der virtuellen Maschine zugeordnet sind, k\u00f6nnen aber auch Installationsmedien sein.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sourceFileHigh=3<\/code><\/pre>\n\n\n\n<p>Danach kann man eine Datei angeben, in die VMs geschrieben werden, die vom Backup ausgeschlossen sein sollen. Ausgehend von dieser Liste wird die Anzahl berechnet.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>excludedVMsFile=\"excludedVMs.txt\"<\/code><\/pre>\n\n\n\n<p>Anschlie\u00dfend wird darauf eingegangen, dass die Daten, die man zieht lokalisiert ausgegeben werden. Diese sollte man der entsprechenden Lokalisation anpassen.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>lastModifiedFile=\"Modifiziert\"\nlastChangedFile=\"Ge\u00e4ndert\"\nbirthOfFile=\"Geburt\"<\/code><\/pre>\n\n\n\n<p>Nun kommen noch die Abfragen zu einigen Verzeichnissen, und die Daten f\u00fcr den externen Backup-PC.<\/p>\n\n\n\n<p>Und damit hat das Skript auch schon alles, was es ben\u00f6tigt.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Establish connection to backup-server\n\nping -c 1 $externalIP  &amp;> \/dev\/null\npingStatus1=$( echo $? )\nif &#91;&#91; $pingStatus1 == 0 ]]\nthen\n\techo \"Backup-Server online, establishing connection\"\n\tmountPoint=\"$externalUserName@$externalIP:$externalBackupDir $localMntDir\"\n\tsshfs $mountPoint\n\tsleep 5s\n\t#exit\nelse\n\techo \"Backup-Server offline, trying to start it now ...\"\n\twakeonlan $macAddress\n\tsleep 120s\n\tping -c 1 $externalIP &amp;> \/dev\/null\n\tpingStatus2=$( echo $? )\n\tif &#91;&#91; $pingStatus2 == 0 ]]\n\tthen\n\t\techo \"Backup-Server woken up, establishing connection\"\n\t\tmountPoint=\"$externalUserName@$externalIP:$externalBackupDir $localMntDir\"\n\t\tsshfs $mountPoint\n\t\tsleep 5s\n\t\t#exit\n\telse\n\t\techo \"No Connection to the back-server, exiting ...\"\n\t\texit\n\tfi\nfi<\/code><\/pre>\n\n\n\n<p>Als Erstes stellt das Skript dann eine Verbindung zum Backup-Server her. Sollte diese nicht gegeben sein, wird es auch nichts mit dem Backup. Der Server wird auf Verf\u00fcgbarkeit gepr\u00fcft, und wenn dies nicht der Fall ist (wovon wir eigentlich ausgehen, aber man wei\u00df ja nie), wird dieser via WakeOnLan gestartet.<\/p>\n\n\n\n<p>Danach wird 2 Minuten gewartet, um dann eine erneute online Pr\u00fcfung zu starten. Sp\u00e4testens zu diesem Zeitpunkt sollte er da sein, und das externe Verzeichnis wird lokal gemountet.<\/p>\n\n\n\n<p>Sollte zu diesem Zeitpunkt immer noch keine Verbindung bestehen, dann bricht das Skript an dieser Stelle ab.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Let's check if the local backup directory excists, and, if not, create it\n\nif &#91;&#91; -d \"$localBackupDir\" ]]\nthen\n\techo \"local backup directory exists, nothing to do here ...\"\nelse\n\techo \"local backup directory doesn't exist, creating ...\"\n\tmkdir \"$localBackupDir\"\nfi<\/code><\/pre>\n\n\n\n<p>Als N\u00e4chstes wird das lokale Backup Verzeichnis erstellt. Moment mal: Lokales Backup-Verzeichnis? Keine Panik, da wir eh mit Text-basierten Dateien arbeiten, ist dies ein Feature, welches uns zu einem sp\u00e4teren Zeitpunkt noch gelegen kommen wird.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Let's check if the Exclusion File is found\n\nif &#91; -f $excludedVMsFile ]\nthen\n\techo \"found exclusion file\"\nfi\n\n# Let's name our VM textfile\nvmTextFile=\"listOfXMLs.txt\"\n\n# Remove any list of XMLs, if there is any\n\nif &#91; -f $vmTextFile ]\nthen\n\techo \"$vmTextFile exists, removing ...\"\n\trm $vmTextFile\nfi\n\n\n# Find all XMLs in the given XMLDirectory, and make a List of them\nfind $xmlDir*.xml | rev | cut -d '\/' -f1 | rev > $vmTextFile\n\n# count the Number of XMLs, e.g. the Number of virtual machines\n\nnrOfXMLs=$(wc -l &lt; $vmTextFile)\n\n# Let's create a file with the current running VMs\n\nrunningVMs=\"runningVMs.txt\"\n\n# remove any list of running VMs, if there is any\n\nif &#91; -f $runningVMs ]\nthen\n\techo \"$runningVMs exists, removing ...\"\n\trm $runningVMs\nfi  \n\nps -ef | grep qemu-system-x86_64 | cut -d',' -f1 | grep name | cut -d'=' -f2 > \"$runningVMs\"\n\n# Let's see how many running VMs we have\n\nnrOfRunningVMs=$(ps -ef | grep qemu-system-x86_64 | cut -d',' -f1 | grep name | cut -d'=' -f2 | wc -l)<\/code><\/pre>\n\n\n\n<p>Nun geht es um die VMs. Als erstes wird \u00fcberpr\u00fcft, ob es eine Ausschlussliste gibt.<\/p>\n\n\n\n<p>Dann wird die VM-Textdatei deklariert, gepr\u00fcft ob sie bereits existiert (was sie nicht sollte, deswegen werden Vorg\u00e4nger &#8211; Versionen auch gel\u00f6scht), und dann wurden alle XML-Dateien im XML Vereichnis gesucht und als Liste in die Datei gepackt. Anschlie\u00dfend wird die Anzahl ermittelt.<\/p>\n\n\n\n<p>Und bevor wir uns jetzt in die Schleife st\u00fcrzen, wird auch noch eine Liste aller laufenden VMs erstellt. Denn, sollten diese laufen, m\u00fcssen sie nachher zumindest pausiert werden, damit es keine Inkonsistenten bei den Daten gibt. Auch hier wird die Anzah erneut ermittelt.<\/p>\n\n\n\n<p>Dann beginnt die for Schleife &#8230;.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>for (( i=1 ; i&lt;=$nrOfXMLs ; i++ ))\ndo \n\t# the current VM\n\tcurrentXML=$(sed \"${i}q;d\" $vmTextFile)\n\t\n\t# We grab the Name of the current VM, and cut loose all the shit that thing doesn't need.\n\tnameOfVM=$(xmllint \"$xmlDir$currentXML\" | grep '&lt;name>' | cut -d '>' -f2 | rev | cut -d '&lt;' -f2 | rev)\n\n\t# Check if the VM to backup isn't exscluded.\n\tif &#91; -f $excludedVMsFile ]\n\tthen\n\t\tfor (( n = 1; n &lt;= $nrOfExcludedVMs; n++ )) \n\t\tdo\n\t\t\texcludedVM=$(sed \"${n}q;d\" $excludedVMsFile)\n\t\t\tif &#91;&#91; $nameOfVM = $excludedVM ]]\n\t\t\tthen\n\t\t\t\techo \"Nothing to do with $nameOfVM\"\n\t\t\t\tbreak\n\t\t\tfi\n\t\tdone\n\telse\n\t\techo \"$nameOfVM is not excluded, continuing ...\"\n\tfi\n\tif &#91;&#91; $nameOfVM = $excludedVM ]]\n\tthen\n\t\techo \"$nameOfVM has been excluded\"\n\t\tcontinue\n\telif &#91;&#91; $nameOfVM != $excludedVM ]]\n\tthen\n\t\tif &#91;&#91; $nrOfRunningVMs -ge 1 ]]\n\t\tthen \n\t\t\tfor (( x = 1; x &lt;= $nrOfRunningVMs; x++ ))\n\t\t\tdo \n\t\t\t\tcurrentRunningVM=$(sed \"${x}q;d\" $runningVMs)\n\t\t\t\tif &#91;&#91; $nameOfVM = $currentRunningVM ]]\n\t\t\t\tthen\n\t\t\t\t\techo \"$nameOfVM is currently running, suspending ...\"\n\t\t\t\t\tvirsh suspend $nameOfVM\n\t\t\t\t\tbreak\n\t\t\t\tfi\n\t\t\tdone\n\t\tfi<\/code><\/pre>\n\n\n\n<p>Die Hauptiteration erfolgt durch die Liste an XMLs, die vorher erstellt wurde. Damit sollten alle VMs aufgefangen werden.<\/p>\n\n\n\n<p>Zun\u00e4chst wird die zuegh\u00f6rige XML Datei herausgeuchst, und in ihr nach dem Namen der VM gesucht, und dieser dann extrahiert.<\/p>\n\n\n\n<p>Nach einer proforma-Abfrage ob die Ausschlussdatei existiert, wird diese dann mit dem Namen der jetzigen VM verglichen. Sollte es eine \u00dcbereinstimmung geben, wird aus der schleife ausgebrochen.<\/p>\n\n\n\n<p>Der Vergleich wird nochmal herangezogen, aber diesmal mit dem Unterschied, dass die Hauptiteration nicht verlassen wird, sondern einfach einen Z\u00e4hler heraufgesetzt wird, und somit die restlichen Anweisungen in der Schleife f\u00fcr diese VMeinfach nicht zum tragen kommen. Sie war ja ausgeschlossen.<\/p>\n\n\n\n<p>Anschlie\u00dfend gibt es die proforma-Abfrage umgekehrt, und dann wird \u00fcberpr\u00fcft, ob es sich um eine laufende VM handelt. Diese wird dann angehalten, um die Datenkonsistenz zu garantieren. Nach dem Anhalten wird aus der dazugeh\u00f6rigen Schleife ausgebrochen.<\/p>\n\n\n\n<p>Alles was jetzt kommt, wird mit jeder VM gemacht, es sei denn, sie ist von vorneherein ausgeschlossen.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># bash in itself doesn't let one operate with objects, so we are circumventing this \n\t\t# right now by creating txt-based backupfiles\n\t\n\t\t# We create a current backupfile\n\t\tcurrentBackupFile=\"$nameOfVM\"-Backup.txt\"\"\n\n\t\tcurrentFileSourceFile=\"$nameOfVM\"-SourceFile.txt\"\"\n\t\n\t\t# The current Backupfile is obsolete, so we are deleting it\n\t\tif &#91;&#91; -f $currentBackupFile ]]\n\t\tthen \n\t\t\trm \"$currentBackupFile\"\n\t\tfi\n\t\n\t\t# The name of the VM is the first line in the newly created backup file\n\t\techo \"$nameOfVM\" > \"$currentBackupFile\"\n\t\n\t\t# The XML file (including the directory path) is the second line in the backup file\n\t\techo \"\"XML File=\"$xmlDir$currentXML\" >> \"$currentBackupFile\"\n\t\n\t\t# Determining how many source files are there, e.g. we count the number of sorce files. Source files may include the \n\t\t# installation disk (mostly .iso-files),\n\t\t# but they may also include different hard disks (qcow2-files). That's why we set the number earlier\n\t\t\n\t\tcurrentNrOfSourceFiles=$(xmllint \"$xmlDir$currentXML\" | grep 'source file' | wc -l)\n\t\t\n\t\tcurrentSourceFiles=$(xmllint \"$xmlDir$currentXML\" | grep 'source file')\n\n\t\tif &#91;&#91; -f $currentFileSourceFile ]]\n\t\tthen\n\t\t\trm \"$currentFileSourceFile\"\n\t\tfi\n\n\t\t# declare k and l, which will incrememnted by 2 every time in the loop, to cut out the path for the different source files\n\t\tk=1\n\t\tl=2\t\n\n\t\t# Now comes the other for loop, in which we will itereate through the different source files. See above\n\n\t\tfor (( j=1 ; j&lt;=$sourceFileHigh ; j++  ))\n\t\tdo\t\t\t\n\n\t\t\tcutSourceFile=$(echo $currentSourceFiles | grep 'source file' | cut -d \" \" -f${k}-${l}) \n\t\t\tmodSourceFile=$(echo $cutSourceFile | cut -d \"=\" -f2 | cut -d '\"' -f2) #| cut -d \">\" -f1 | rev | cut -d \"\/\" -f2 | rev | cut -d '\"' -f1)\n\t\t\t\n\t\t\t# if the source file is empty, just put a blank line in there\n\t\t\tif &#91;&#91; -z $modSourceFile ]]\n\t\t\tthen\n\t\t\t\techo \"$modSourceFile\" >> \"$currentBackupFile\"\n\t\t\t# else put the following info in there\n\t\t\telse\n\t\t\t\techo \"\"Source Path=\"$modSourceFile\" >> \"$currentBackupFile\"\n\n\t\t\t\techo \"$modSourceFile\" >> \"$currentFileSourceFile\"\n\t\t\t\t  \n\t\t\t\t# now let's grep the info and store it in a file\n\t\t\t\tmodifySourceFile=$(stat $modSourceFile |  grep $lastModifiedFile | cut -d \":\" -f2-4 | cut -d \" \" -f2-4)\n\t\t\t\techo \"\"Last Modified=\"$modifySourceFile\" >> \"$currentBackupFile\"\n\t\t\t\tchangeSourceFile=$(stat $modSourceFile |  grep $lastChangedFile | cut -d \":\" -f2-4 | cut -d \" \" -f2-4)\n\t\t\t\techo \"\"Last Changed=\"$changeSourceFile\" >> \"$currentBackupFile\"\t\t\t\n\t\t\t\tbirthOfSourceFile=$(stat $modSourceFile |  grep $birthOfFile | cut -d \":\" -f2-4 | cut -d \" \" -f2-4)\n\t\t\t\techo \"\"Birth Of File=\"$birthOfSourceFile\" >> \"$currentBackupFile\"\n\n\t\t\tfi\n\t\n\n\t\t\t# k needs to be incremented by 2\n\t\t\tk=$((k+2))\n\n\t\t\t# l needs to be incremented by 2\n\t\t\tl=$((l+2))\n\t\t\n\t\tdone\n\n\t\t# Last line in file\n\t\techo \"End of File\" >> \"$currentBackupFile\"<\/code><\/pre>\n\n\n\n<p>Es werden zwei Dateien kreiert, zum einen die Backup-Datei, zum anderen die Quellen-Datei.<\/p>\n\n\n\n<p>Erstere spielt l\u00e4ngerfristig eine Rolle, die zweite nur tempor\u00e4r.<\/p>\n\n\n\n<p>Es erfolgt die Standardm\u00e4\u00dfige \u00fcberpr\u00fcfung, ob sie existiert.<\/p>\n\n\n\n<p>Dann wird angefangen die Backup-Datei zu schreiben.<\/p>\n\n\n\n<p>Daten in ihr:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Name\nXML Datei mit Pfad\nQuell-Datei-1 mit Pfad\nDatum der letzen Modifizierung von Quell-Datei-1\nDatum der letzte \u00c4nderung von Quell-Datei-1\nDatum der Erschaffung von Quell-Datei-1\n# Bei mehreren Quell-Dateien\nQuell-Datei-2 mit Pfad\nDatum der letzen Modifizierung von Quell-Datei-2\nDatum der letzte \u00c4nderung von Quell-Datei-2\nDatum der Erschaffung von Quell-Datei-2\n\nEnde der Datei<\/code><\/pre>\n\n\n\n<p>Urspr\u00fcnglich war Zugriff mit in der Datei, aber da dieser sich bei jedem Kopiervorgang \u00e4ndert, wurde dieser entfernt.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Check if the local backup directory exists, and, if it doesn't, create it\n\t\tif &#91;&#91; -d \"$localBackupDir\/$nameOfVM\" ]]\n\t\tthen\n\t\t\techo \"local backup directory for $nameOfVM exists, nothing to do here ...\"\n\t\telse\n\t\t\techo \"local backup directory for $nameOfVM doesn't exist, creating ...\"\n\t\t\tmkdir \"$localBackupDir\/$nameOfVM\"\n\t\tfi<\/code><\/pre>\n\n\n\n<p>Ein Verzeichnis mit Namen der aktuellen VM wird lokal erstellt, sollte es nicht existieren, um darin die Backup-Datei zu speichern.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Let's check if there is a backup file already and compare it to the current file\n    # Since the we just accumulated all the information about the VM, we check if the VM has been        started in any way, \n    # and if it hasn't, we won't need to do anything.\n    # In the aforementioned case, the loop will stop here and continue to the next VM\n\n    if &#91;&#91; -f \"$localBackupDir\/$nameOfVM\/$currentBackupFile\" ]]       \n    then\n        if &#91;&#91; 'cmp \"$localBackupDir\/$nameOfVM\/$currentBackupFile\" $currentBackupFile' ]]\n        then\n            echo \"The VM hasn't changed since the last backup, no need to do anything ...\"\n            rm \"$currentBackupFile\"\n            rm \"$currentFileSourceFile\"\n            continue\n        else\n            echo \"The VM has changed, removing old backup file ...\"\n            rm \"$localBackupDir\/$nameOfVM\/$currentBackupFile\"\n            echo \"Writing new local backup file\"\n            rsync --progress \"$currentBackupFile\" \"$localBackupDir\/$nameOfVM\/$currentBackupFile\"\n        fi\n    else\n        echo \"The backup file doesn't exist, creating ...\"\n        rsync --progress \"$currentBackupFile\" \"$localBackupDir\/$nameOfVM\/$currentBackupFile\"\n    fi<\/code><\/pre>\n\n\n\n<p>Es erfolgt der Vergleich (sofern schon eine Backup-Datei existiert) zwischen der gerade erstellten und der gespeicherten Backup-Datei. Sollten diese identisch sein, wird auf das Backup verzichtet, und mit der n\u00e4chsten VM weitergemacht. Es lohnt sich nicht, die VM, die bereits auf der backup-Platte existiert, erneut zu backupsichernen. F\u00fcr diesen Vergleich existiert die Backup-Datei.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if &#91;&#91; -d \"$localMntDir\/$nameOfVM\" ]]\n\t\t\tthen\n\t\t\t\techo \"Directory for $nameOfVM exists, nothing to do here ...\"\n\t\t\telse\n\t\t\t\techo \"Directory for $nameOfVM doesn't exist, creating ...\"\n\t\t\t\tmkdir \"$localMntDir\/$nameOfVM\"\n\t\t\tfi\n\n\t\tdate=\"$(date '+%Y-%m-%d')\"\n\n\t\t# This checks if the backup was done today, and if it was, it assumes that something went wrong \n\t\t# (usually there are not two backups in a single day,)\n\t\t# If this is the second backup in one day, it removes everything.\n\t\t\t\n\t\tif &#91;&#91; -d \"$localMntDir\/$nameOfVM\/$date\" ]]\n\t\tthen\n\t\t\techo \"Todays date directory exists, cleaning it up ...\"\n\t\t\tif &#91;&#91; -f \"$localMntDir\/$nameOfVM\/$date\/$currentXML\" ]]\n\t\t\tthen\n\t\t\t\techo \"$currentXML exists, removing ...\"\n\t\t\t\trm -v \"$localMntDir\/$nameOfVM\/$date\/$currentXML\"\n\t\t\tfi\n\t\t\tif &#91;&#91; -f \"$localMntDir\/$nameOfVM\/$date\/$currentBackupFile\" ]]\n\t\t\tthen\n\t\t\t\techo \"$currentBackupFile exists, removing ...\"\n\t\t\t\trm -v \"$localMntDir\/$nameOfVM\/$date\/$currentBackupFile\"\n\t\t\tfi\n\t\telse\n\t\t\techo \"Todays date directory doesn't exist, creating ...\"\n\t\t\tmkdir \"$localMntDir\/$nameOfVM\/$date\"\n\t\tfi\n\n\t\t# Last but not least, the copying process of the needed files\n\n\t\t\tif &#91;&#91; -d \"$localMntDir\/$nameOfVM\/$date\" ]]\n\t\t\tthen\n\t\t\t\techo \" Copying text files to backup destination ...\"\n\t\t\t\trsync --progress \"$xmlDir$currentXML\" \"$localMntDir\/$nameOfVM\/$date\/\"\n\t\t\t\trsync --progress \"$currentBackupFile\" \"$localMntDir\/$nameOfVM\/$date\/\"\n\t\t\tfi\n\n\t\tsleep 10s<\/code><\/pre>\n\n\n\n<p>Jetzt wird Verzeichnis auf dem gemounteten Datentr\u00e4ger kreiert, auch wieder mit Namen der VM. Hinzu kommt ein Verzeichnis im Datumsformat. Sollte es (aus Gr\u00fcnden) zwei Backups am selben Tag geben, ist davon auszugehen, dass etwas schief gelaufen ist. In dem fall werden die vorhandenen daten gel\u00f6scht.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>nrOfSourceFiles=$(wc -l &lt; $currentFileSourceFile)\n\n\t\tfor (( z = 1; z &lt;= $nrOfSourceFiles; z++ ))\n\t\tdo\n\t\t\tcurrentQcow2=$(sed -n \"${z}p\" \"$currentFileSourceFile\")\n\t\t\tcurrentQcow2File=$(sed -n \"${z}p\" \"$currentFileSourceFile\" | rev | cut -d '\/' -f1 | rev)\n\n\t\t\tif &#91;&#91; -f \"$localMntDir\/$nameOfVM\/$date\/$currentQcow2File\" ]]\n\t\t\tthen\n\t\t\t\techo \"$currentQcow2 exists, removing ...\"\n\t\t\t\trm -v \"$localMntDir\/$nameOfVM\/$date\/$currentQcow2File\"\t\n\t\t\tfi\n\n\t\t\tif &#91;&#91; -d \"$localMntDir\/$nameOfVM\/$date\" ]]\n\t\t\tthen\n\t\t\t\techo \" Copying $currentQcow2 files to backup destination ...\"\n\t\t\t\trsync --progress \"$currentQcow2\" \"$localMntDir\/$nameOfVM\/$date\/\"\n\t\t\tfi\n\t\t\t\n\t\tdone<\/code><\/pre>\n\n\n\n<p>Nun wird die Quellen-Datei untersucht, und alle in ihr befindlichen Dateien in das neue Verzeichnis kopiert. Auch hier: Sollte dies am selben Tag passieren, werden die Daten zuvor gel\u00f6scht.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Cleanup ...\n\n\t\techo \"removing created files ...\"\n\n\t\trm \"$currentBackupFile\"\n\t\trm \"$currentFileSourceFile\"\n\n\t\t# We need to resume the VM, if it was running in the first place ...\n\n\t\tif &#91;&#91; $nameOfVM = $currentRunningVM ]]\n\t\tthen\n\t\t\techo \"$nameOfVM needs to be resumed!\"\n\t\t\tvirsh resume $nameOfVM\n\t\tfi\n\tfi\ndone\n\n# Cleanup ...\n\nrm \"$vmTextFile\"\nrm \"$runningVMs\"\n\n# Unmount the sshfs directory\n\nfusermount -u $localMntDir\n\n# shutdown the remote backup-server\n\nssh \"$externalUserName@$externalIP\" \"sudo shutdown now\"<\/code><\/pre>\n\n\n\n<p>Abschlie\u00dfend wird aufger\u00e4umt. S\u00e4mtliche erstellten Dateien werden gel\u00f6scht, und sobald die Hauptschleife verlassen wurde, wird das lokal gemountete Verzeichnis wieder freigegeben. Somit bleibt nur noch eines: den Backup-Server herunterzufahren.<\/p>\n\n\n\n<p>Das Script war so im Einsatz und hat zwei so gewollte Sicherungen generiert. Aber es ist definitiv noch nicht perfekt. Der Existenz-Abgleich der Dateien k\u00f6nnte noch besser werden, und es gibt noch kein &#8222;Packen&#8220; der VMs, d.h. sie werden in ihrer urspr\u00fcnglichen Gr\u00f6\u00dfe transferiert.<\/p>\n\n\n\n<p>Dies hat aber folgende Gr\u00fcnde: Ich hatte den Platz auf der dem Backup-Server, und ich wollte die Sicherung in einem angemessenen Zeitrahmen vonstatten bringen. Die Sicherung aller VMs hat mich so schon ca. 36 Stunden gekostet, mit &#8222;Packen&#8220; h\u00e4tte das l\u00e4nger gedauert. <\/p>\n\n\n\n<p>F\u00fcr die Zukunft ziehe ich das aber in Betracht. <\/p>\n\n\n\n<p>Und das Script, mit ein paar Modifikationen, wird dabei helfen. <\/p>\n\n\n\n<p>Je nach Wochentag kann ich eine bestimmte Liste an VMs sichern, indem ich den Rest einfach auf die &#8222;nicht sichern Liste&#8220; setze, und diese kann ich dann auch packen. Dann habe nehme ich mir das Zeitfenster von zwei bis sechs Uhr morgens, und sichere diese dann zu diesem Zeitpunkt weg.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Der Neuaufbau eines Servers erfordert immer Planung. Gibt es \u00c4nderungen in der bereitgestellten Hardware, gibt es neue und vor allem bessere Software zum Erreichen der Ziele? Aber zun\u00e4chst gilt es die Daten, welche anschlie\u00dfend noch ben\u00f6tigt werden, zu sichern. Das hei\u00dft, das Backup sollte vollumf\u00e4nglich sein. In meinem Fall war dies zun\u00e4chst die Sicherung der &hellip; <\/p>\n<p><a class=\"more-link btn\" href=\"https:\/\/www.florian-klaus.de\/index.php\/2025\/04\/16\/umzug-des-servers-von-ubuntu-auf-debian\/\">Continue reading<\/a><\/p>\n","protected":false},"author":1,"featured_media":382,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[12],"tags":[],"class_list":["post-379","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-it_themen","item-wrap"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v25.6 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Umzug des Servers von Ubuntu auf Debian - www.florian-klaus.de<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.florian-klaus.de\/index.php\/2025\/04\/16\/umzug-des-servers-von-ubuntu-auf-debian\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Umzug des Servers von Ubuntu auf Debian - www.florian-klaus.de\" \/>\n<meta property=\"og:description\" content=\"Der Neuaufbau eines Servers erfordert immer Planung. Gibt es \u00c4nderungen in der bereitgestellten Hardware, gibt es neue und vor allem bessere Software zum Erreichen der Ziele? Aber zun\u00e4chst gilt es die Daten, welche anschlie\u00dfend noch ben\u00f6tigt werden, zu sichern. Das hei\u00dft, das Backup sollte vollumf\u00e4nglich sein. In meinem Fall war dies zun\u00e4chst die Sicherung der &hellip; Continue reading\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.florian-klaus.de\/index.php\/2025\/04\/16\/umzug-des-servers-von-ubuntu-auf-debian\/\" \/>\n<meta property=\"og:site_name\" content=\"www.florian-klaus.de\" \/>\n<meta property=\"article:published_time\" content=\"2025-04-16T10:00:41+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-04-16T10:09:18+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.florian-klaus.de\/wp-content\/uploads\/2025\/04\/Hdd.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"2049\" \/>\n\t<meta property=\"og:image:height\" content=\"1061\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Florian Klaus\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@bballjunkieflow\" \/>\n<meta name=\"twitter:site\" content=\"@bballjunkieflow\" \/>\n<meta name=\"twitter:label1\" content=\"Geschrieben von\" \/>\n\t<meta name=\"twitter:data1\" content=\"Florian Klaus\" \/>\n\t<meta name=\"twitter:label2\" content=\"Gesch\u00e4tzte Lesezeit\" \/>\n\t<meta name=\"twitter:data2\" content=\"9\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.florian-klaus.de\/index.php\/2025\/04\/16\/umzug-des-servers-von-ubuntu-auf-debian\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.florian-klaus.de\/index.php\/2025\/04\/16\/umzug-des-servers-von-ubuntu-auf-debian\/\"},\"author\":{\"name\":\"Florian Klaus\",\"@id\":\"https:\/\/www.florian-klaus.de\/#\/schema\/person\/e42d488306a14de51fae9c2aa19f95a9\"},\"headline\":\"Umzug des Servers von Ubuntu auf Debian\",\"datePublished\":\"2025-04-16T10:00:41+00:00\",\"dateModified\":\"2025-04-16T10:09:18+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.florian-klaus.de\/index.php\/2025\/04\/16\/umzug-des-servers-von-ubuntu-auf-debian\/\"},\"wordCount\":1535,\"publisher\":{\"@id\":\"https:\/\/www.florian-klaus.de\/#\/schema\/person\/e42d488306a14de51fae9c2aa19f95a9\"},\"image\":{\"@id\":\"https:\/\/www.florian-klaus.de\/index.php\/2025\/04\/16\/umzug-des-servers-von-ubuntu-auf-debian\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.florian-klaus.de\/wp-content\/uploads\/2025\/04\/Hdd.jpg\",\"articleSection\":[\"IT-Themen\"],\"inLanguage\":\"de\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.florian-klaus.de\/index.php\/2025\/04\/16\/umzug-des-servers-von-ubuntu-auf-debian\/\",\"url\":\"https:\/\/www.florian-klaus.de\/index.php\/2025\/04\/16\/umzug-des-servers-von-ubuntu-auf-debian\/\",\"name\":\"Umzug des Servers von Ubuntu auf Debian - www.florian-klaus.de\",\"isPartOf\":{\"@id\":\"https:\/\/www.florian-klaus.de\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.florian-klaus.de\/index.php\/2025\/04\/16\/umzug-des-servers-von-ubuntu-auf-debian\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.florian-klaus.de\/index.php\/2025\/04\/16\/umzug-des-servers-von-ubuntu-auf-debian\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.florian-klaus.de\/wp-content\/uploads\/2025\/04\/Hdd.jpg\",\"datePublished\":\"2025-04-16T10:00:41+00:00\",\"dateModified\":\"2025-04-16T10:09:18+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/www.florian-klaus.de\/index.php\/2025\/04\/16\/umzug-des-servers-von-ubuntu-auf-debian\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.florian-klaus.de\/index.php\/2025\/04\/16\/umzug-des-servers-von-ubuntu-auf-debian\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\/\/www.florian-klaus.de\/index.php\/2025\/04\/16\/umzug-des-servers-von-ubuntu-auf-debian\/#primaryimage\",\"url\":\"https:\/\/www.florian-klaus.de\/wp-content\/uploads\/2025\/04\/Hdd.jpg\",\"contentUrl\":\"https:\/\/www.florian-klaus.de\/wp-content\/uploads\/2025\/04\/Hdd.jpg\",\"width\":2049,\"height\":1061},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.florian-klaus.de\/index.php\/2025\/04\/16\/umzug-des-servers-von-ubuntu-auf-debian\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.florian-klaus.de\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Umzug des Servers von Ubuntu auf Debian\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.florian-klaus.de\/#website\",\"url\":\"https:\/\/www.florian-klaus.de\/\",\"name\":\"www.florian-klaus.de\",\"description\":\"self-portrayal streamlined\",\"publisher\":{\"@id\":\"https:\/\/www.florian-klaus.de\/#\/schema\/person\/e42d488306a14de51fae9c2aa19f95a9\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.florian-klaus.de\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"de\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\/\/www.florian-klaus.de\/#\/schema\/person\/e42d488306a14de51fae9c2aa19f95a9\",\"name\":\"Florian Klaus\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\/\/www.florian-klaus.de\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/f6308ba1e8ac765e3b92f1387a1fcc75b30d99c2053687bcc7be52709ca8aea7?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/f6308ba1e8ac765e3b92f1387a1fcc75b30d99c2053687bcc7be52709ca8aea7?s=96&d=mm&r=g\",\"caption\":\"Florian Klaus\"},\"logo\":{\"@id\":\"https:\/\/www.florian-klaus.de\/#\/schema\/person\/image\/\"},\"description\":\"Systeminformatiker, Schwerpunkt Integration. Hobbys: Basketball, Am. Football. E-Sports, Podcasts\",\"sameAs\":[\"https:\/\/www.florian-klaus.de\",\"https:\/\/x.com\/bballjunkieflow\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Umzug des Servers von Ubuntu auf Debian - www.florian-klaus.de","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.florian-klaus.de\/index.php\/2025\/04\/16\/umzug-des-servers-von-ubuntu-auf-debian\/","og_locale":"de_DE","og_type":"article","og_title":"Umzug des Servers von Ubuntu auf Debian - www.florian-klaus.de","og_description":"Der Neuaufbau eines Servers erfordert immer Planung. Gibt es \u00c4nderungen in der bereitgestellten Hardware, gibt es neue und vor allem bessere Software zum Erreichen der Ziele? Aber zun\u00e4chst gilt es die Daten, welche anschlie\u00dfend noch ben\u00f6tigt werden, zu sichern. Das hei\u00dft, das Backup sollte vollumf\u00e4nglich sein. In meinem Fall war dies zun\u00e4chst die Sicherung der &hellip; Continue reading","og_url":"https:\/\/www.florian-klaus.de\/index.php\/2025\/04\/16\/umzug-des-servers-von-ubuntu-auf-debian\/","og_site_name":"www.florian-klaus.de","article_published_time":"2025-04-16T10:00:41+00:00","article_modified_time":"2025-04-16T10:09:18+00:00","og_image":[{"width":2049,"height":1061,"url":"https:\/\/www.florian-klaus.de\/wp-content\/uploads\/2025\/04\/Hdd.jpg","type":"image\/jpeg"}],"author":"Florian Klaus","twitter_card":"summary_large_image","twitter_creator":"@bballjunkieflow","twitter_site":"@bballjunkieflow","twitter_misc":{"Geschrieben von":"Florian Klaus","Gesch\u00e4tzte Lesezeit":"9\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.florian-klaus.de\/index.php\/2025\/04\/16\/umzug-des-servers-von-ubuntu-auf-debian\/#article","isPartOf":{"@id":"https:\/\/www.florian-klaus.de\/index.php\/2025\/04\/16\/umzug-des-servers-von-ubuntu-auf-debian\/"},"author":{"name":"Florian Klaus","@id":"https:\/\/www.florian-klaus.de\/#\/schema\/person\/e42d488306a14de51fae9c2aa19f95a9"},"headline":"Umzug des Servers von Ubuntu auf Debian","datePublished":"2025-04-16T10:00:41+00:00","dateModified":"2025-04-16T10:09:18+00:00","mainEntityOfPage":{"@id":"https:\/\/www.florian-klaus.de\/index.php\/2025\/04\/16\/umzug-des-servers-von-ubuntu-auf-debian\/"},"wordCount":1535,"publisher":{"@id":"https:\/\/www.florian-klaus.de\/#\/schema\/person\/e42d488306a14de51fae9c2aa19f95a9"},"image":{"@id":"https:\/\/www.florian-klaus.de\/index.php\/2025\/04\/16\/umzug-des-servers-von-ubuntu-auf-debian\/#primaryimage"},"thumbnailUrl":"https:\/\/www.florian-klaus.de\/wp-content\/uploads\/2025\/04\/Hdd.jpg","articleSection":["IT-Themen"],"inLanguage":"de"},{"@type":"WebPage","@id":"https:\/\/www.florian-klaus.de\/index.php\/2025\/04\/16\/umzug-des-servers-von-ubuntu-auf-debian\/","url":"https:\/\/www.florian-klaus.de\/index.php\/2025\/04\/16\/umzug-des-servers-von-ubuntu-auf-debian\/","name":"Umzug des Servers von Ubuntu auf Debian - www.florian-klaus.de","isPartOf":{"@id":"https:\/\/www.florian-klaus.de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.florian-klaus.de\/index.php\/2025\/04\/16\/umzug-des-servers-von-ubuntu-auf-debian\/#primaryimage"},"image":{"@id":"https:\/\/www.florian-klaus.de\/index.php\/2025\/04\/16\/umzug-des-servers-von-ubuntu-auf-debian\/#primaryimage"},"thumbnailUrl":"https:\/\/www.florian-klaus.de\/wp-content\/uploads\/2025\/04\/Hdd.jpg","datePublished":"2025-04-16T10:00:41+00:00","dateModified":"2025-04-16T10:09:18+00:00","breadcrumb":{"@id":"https:\/\/www.florian-klaus.de\/index.php\/2025\/04\/16\/umzug-des-servers-von-ubuntu-auf-debian\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.florian-klaus.de\/index.php\/2025\/04\/16\/umzug-des-servers-von-ubuntu-auf-debian\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/www.florian-klaus.de\/index.php\/2025\/04\/16\/umzug-des-servers-von-ubuntu-auf-debian\/#primaryimage","url":"https:\/\/www.florian-klaus.de\/wp-content\/uploads\/2025\/04\/Hdd.jpg","contentUrl":"https:\/\/www.florian-klaus.de\/wp-content\/uploads\/2025\/04\/Hdd.jpg","width":2049,"height":1061},{"@type":"BreadcrumbList","@id":"https:\/\/www.florian-klaus.de\/index.php\/2025\/04\/16\/umzug-des-servers-von-ubuntu-auf-debian\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.florian-klaus.de\/"},{"@type":"ListItem","position":2,"name":"Umzug des Servers von Ubuntu auf Debian"}]},{"@type":"WebSite","@id":"https:\/\/www.florian-klaus.de\/#website","url":"https:\/\/www.florian-klaus.de\/","name":"www.florian-klaus.de","description":"self-portrayal streamlined","publisher":{"@id":"https:\/\/www.florian-klaus.de\/#\/schema\/person\/e42d488306a14de51fae9c2aa19f95a9"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.florian-klaus.de\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"de"},{"@type":["Person","Organization"],"@id":"https:\/\/www.florian-klaus.de\/#\/schema\/person\/e42d488306a14de51fae9c2aa19f95a9","name":"Florian Klaus","image":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/www.florian-klaus.de\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/f6308ba1e8ac765e3b92f1387a1fcc75b30d99c2053687bcc7be52709ca8aea7?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/f6308ba1e8ac765e3b92f1387a1fcc75b30d99c2053687bcc7be52709ca8aea7?s=96&d=mm&r=g","caption":"Florian Klaus"},"logo":{"@id":"https:\/\/www.florian-klaus.de\/#\/schema\/person\/image\/"},"description":"Systeminformatiker, Schwerpunkt Integration. Hobbys: Basketball, Am. Football. E-Sports, Podcasts","sameAs":["https:\/\/www.florian-klaus.de","https:\/\/x.com\/bballjunkieflow"]}]}},"_links":{"self":[{"href":"https:\/\/www.florian-klaus.de\/index.php\/wp-json\/wp\/v2\/posts\/379","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.florian-klaus.de\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.florian-klaus.de\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.florian-klaus.de\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.florian-klaus.de\/index.php\/wp-json\/wp\/v2\/comments?post=379"}],"version-history":[{"count":2,"href":"https:\/\/www.florian-klaus.de\/index.php\/wp-json\/wp\/v2\/posts\/379\/revisions"}],"predecessor-version":[{"id":381,"href":"https:\/\/www.florian-klaus.de\/index.php\/wp-json\/wp\/v2\/posts\/379\/revisions\/381"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.florian-klaus.de\/index.php\/wp-json\/wp\/v2\/media\/382"}],"wp:attachment":[{"href":"https:\/\/www.florian-klaus.de\/index.php\/wp-json\/wp\/v2\/media?parent=379"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.florian-klaus.de\/index.php\/wp-json\/wp\/v2\/categories?post=379"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.florian-klaus.de\/index.php\/wp-json\/wp\/v2\/tags?post=379"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}