<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Welcome to My Homelab!</title>
    <description>This blog is one small step for mankind and one giant leap for me!
</description>
    <link>https://zroupas.github.io/</link>
    <atom:link href="https://zroupas.github.io/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Thu, 09 Apr 2026 07:41:03 +0000</pubDate>
    <lastBuildDate>Thu, 09 Apr 2026 07:41:03 +0000</lastBuildDate>
    <generator>Jekyll v4.4.1</generator>
    
      <item>
        <title>Dotfiles Management with Dotbot and Chezmoi!</title>
        <description>&lt;h1 id=&quot;dotfiles-management-with-dotbot-and-chezmoi&quot;&gt;Dotfiles Management with Dotbot and Chezmoi!&lt;/h1&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Hey everyone! Long time no see!&lt;/p&gt;

&lt;p&gt;As a lazy guy myself, always trying to find ways to automate tasks and minimize, as much as possible, manual actions.&lt;/p&gt;

&lt;p&gt;One of my struggles, is keeping my different macOS machines synced. And by synced I mean from aliases, macOS system settings and dotfiles to gpg/ssh keys, homebrew packages and development tools.&lt;/p&gt;

&lt;p&gt;So you can probably understand the pain of trying to keep the same environment, your commands, your tooling, your git or zsh config, while moving from your personal machine to your work related one.&lt;/p&gt;

&lt;h2 id=&quot;dotfiles&quot;&gt;Dotfiles&lt;/h2&gt;

&lt;p&gt;One of the most important aspects of working across multiple machines, at least for me, is ensuring that my dotfiles are always accessible. In my case, there are 2 different dotfile categories, the &lt;strong&gt;normal&lt;/strong&gt; ones like aliases, vimrc, zshrc etc, which are the same in each machine and the &lt;strong&gt;templated&lt;/strong&gt; ones, as I like to call them.&lt;/p&gt;

&lt;p&gt;By &lt;em&gt;templated&lt;/em&gt; I mean dotfiles that should be different for each machine. For example, I don’t want my &lt;em&gt;.netrc&lt;/em&gt; to have work related tokens. Also I don’t need a &lt;em&gt;.terraformrc&lt;/em&gt; in my personal machine.&lt;/p&gt;

&lt;p&gt;Haven’t found any dotfile manager that handles both cases, so I decided to combine two different solutions to be able to keep my systems synced.&lt;/p&gt;

&lt;h3 id=&quot;dotbot&quot;&gt;Dotbot&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/anishathalye/dotbot&quot;&gt;Dotbot&lt;/a&gt; is an awesome tool that I’m using for the &lt;em&gt;non-templated&lt;/em&gt; dotfiles. It’s very easy and straight forward to setup and use.&lt;/p&gt;

&lt;p&gt;In my setup, I have a repo where dotbot is initialized and my dotfiles are present. The folder structure looks like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;├── centos/
│   ├── dotfiles/
├── common/
│   ├── dotfiles/
│   └── vim/
├── dotbot/
│   ├── bin/
│   ├── dotbot/
│   ├── lib/
│   ├── tests/
│   ├── tools/
│   ├── CHANGELOG.md
│   ├── CONTRIBUTING.md
│   ├── LICENSE.md
│   ├── pyproject.toml
│   ├── README.md
│   ├── setup.py
│   └── tox.ini
├── mac/
│   ├── docker/
│   ├── dotfiles/
│   ├── gnupg/
│   ├── launchagents/
│   ├── m2/
│   └── vim_runtime/
├── install*
├── install.config.yaml
└── README.md
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can see, I have separated files in different folders,&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;centos folder, linux specific dotfiles  in case I want to bootstrap a linux machine&lt;/li&gt;
  &lt;li&gt;common folder, dotfiles that are relevant for both linux and mac&lt;/li&gt;
  &lt;li&gt;dobot folder, is created from dotbot installation (check https://github.com/anishathalye/dotbot?tab=readme-ov-file#getting-started)&lt;/li&gt;
  &lt;li&gt;mac folder, mac specific dotfiles and other config files&lt;/li&gt;
  &lt;li&gt;install , script that will install the dotfiles&lt;/li&gt;
  &lt;li&gt;install.config.yaml , config used by the installer to create the relevant symlinks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;strong&gt;install.config.yaml&lt;/strong&gt; looks like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# cleans deadlinks , useful if we want to delete where we can rename the main folders and run dotbot to clean deadlinks
- clean: [&apos;~&apos;]

- defaults:
    link:
      relink: true
      force: true

- link:
    ~/:
      glob: true
      path: common/dotfiles/.*
    ~/.vim/templates:
      glob: true
      path: common/vim/*
      create: true

- defaults:
    link:
      if: &apos;[ `uname` = Darwin ]&apos;
      relink: true
      force: true

- link:
    ~/:
      glob: true
      path: mac/dotfiles/.*
    ~/.docker:
      glob: true
      path: mac/docker/*
      create: true
    ~/.gnupg:
      glob: true
      path: mac/gnupg/*
    ~/.vim_runtime:
      glob: true
      path: mac/vim_runtime/*
    ~/Library/LaunchAgents:
      glob: true
      path: mac/launchagents/*
      create: true

- shell:
  - [launchctl unload ~/Library/LaunchAgents/]
  - [launchctl load ~/Library/LaunchAgents/]

- defaults:
    link:
      if: &apos;[ `uname` = Linux ]&apos;
      relink: true
      force: true

- link:
    ~/:
      glob: true
      path: centos/dotfiles/.*
      create: true

- shell:
  - [git submodule update --init --recursive, Installing submodules]

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can easily see from the config, if executed it will create symbolic links at specified locations that point to files in my dotfiles repository.&lt;/p&gt;

&lt;p&gt;There is a separation between operating systems, Linux and Darwin in my case, and some shell commands that are specific to my needs, but you can adapt it accordingly.&lt;/p&gt;

&lt;p&gt;So after executing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./install -c install.config.yaml&lt;/code&gt;,  you should something like that in your home folder :&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;lrwxr-xr-x     - testuser 26 Jun  2025 .zshr -&amp;gt; /Users/path/to/your/repo/mac/dotfiles/.zshr
lrwxr-xr-x     - testuser 26 Jun  2025 .zshrc -&amp;gt; /Users/path/to/your/repo/mac/dotfiles/.zshrc
lrwxr-xr-x     - testuser 26 Jun  2025 .gitignore_global -&amp;gt; /Users/path/to/your/repo/common/dotfiles/.gitignore_global
lrwxr-xr-x     - testuser 26 Jun  2025 .fzf.zsh -&amp;gt; /Users/path/to/your/repo/mac/dotfiles/.fzf.zsh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is a rough explanation of how I’m handling &lt;em&gt;non-templated&lt;/em&gt; files via Dotbot.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;[!TIP]
As my repo keeps sensitive data, like docker auth, gitlab tokens etc, I use &lt;a href=&quot;https://github.com/elasticdog/transcrypt&quot;&gt;transcrypt&lt;/a&gt; to encrypt such data.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;chezmoi&quot;&gt;Chezmoi&lt;/h3&gt;

&lt;p&gt;Chezmoi is another awesome highly customizable tool that manages my dotfiles across multiple different machines, in a secure way.&lt;/p&gt;

&lt;p&gt;Chezmoi documentation is very comprehensive, so I won’t spend time explaining the initial setup but you can start &lt;a href=&quot;https://www.chezmoi.io/user-guide/setup/&quot;&gt;here&lt;/a&gt;. It supports &lt;a href=&quot;https://www.chezmoi.io/user-guide/encryption/&quot;&gt;encryption&lt;/a&gt; and also &lt;a href=&quot;https://www.chezmoi.io/user-guide/templating/&quot;&gt;templating&lt;/a&gt; which is my favorite part! 
Have a look on the official official &lt;a href=&quot;https://www.chezmoi.io/user-guide/command-overview/&quot;&gt;User Guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For my setup, I have yet another repo 😆 which is initialized via chezmoi and folder structure looks like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;├── private_dot_ssh/
│   ├── encrypted_private_executable_config.tmpl.age
│   ├── encrypted_private_executable_id_rsa.tmpl.age
│   └── private_executable_id_rsa.pub.tmpl
├── dot_gitconfig.tmpl
├── dot_vimrc.tmpl
├── encrypted_dot_netrc.tmpl.age
├── encrypted_dot_terraformrc.tmpl.age
├── Makefile
└── README.md
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now lets see how my &lt;em&gt;.terraformrc&lt;/em&gt; looks like:
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;❯ chezmoi edit ~/.terraformrc&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;credentials &quot;gitlab.com&quot; {
   token = &quot;&amp;lt;REDUCTED&amp;gt;&quot;
 }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With chezmoi’s templating power, a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.terraformrc&lt;/code&gt; file will only be created if the user of the machine that I’m running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chezmoi apply&lt;/code&gt; is &lt;strong&gt;customuser&lt;/strong&gt; . Otherwise, no such file will be created.&lt;/p&gt;

&lt;p&gt;The above is just an example and you can use the automatically-populated available variables found &lt;a href=&quot;https://www.chezmoi.io/reference/templates/variables/&quot;&gt;here&lt;/a&gt;,based on your setup.&lt;/p&gt;

&lt;h2 id=&quot;automation&quot;&gt;Automation&lt;/h2&gt;

&lt;p&gt;Dotbot and Chezmoi managers can be of course configured and used independently of each other as well as standalone.&lt;/p&gt;

&lt;p&gt;Since I wanted to automate the process, I created &lt;a href=&quot;https://github.com/roupasz/MacOS-Ansible-Bootstrap&quot;&gt;MacOS-Ansible-Bootstrap&lt;/a&gt;repository which uses both dotfiles managers to configure the files needed in every machine.&lt;/p&gt;

&lt;p&gt;In addition, the repository makes use of Bash and Ansible to configure macOS system settings, import gpg keys, install homebrew packages and plugins with the help of &lt;a href=&quot;https://bitwarden.com/&quot;&gt;Bitwarden&lt;/a&gt;, my favorite Password Manager!&lt;/p&gt;

&lt;p&gt;You can check the repo, adapt it accordingly and star it if you like it! 😉&lt;/p&gt;

&lt;h2 id=&quot;final-words-&quot;&gt;Final Words 💡&lt;/h2&gt;

&lt;p&gt;Let’s be honest, dotfile managers are awesome but all the heavy work is done from git.&lt;/p&gt;

&lt;p&gt;Having my most useful files encrypted and tracked in a repository, significantly saves time both productivity and troubleshooting wise.&lt;/p&gt;

&lt;p&gt;These awesome tools then act as wrappers adding advanced features that handle file symlinking automatically, saving me the time and effort of writing custom scripts.&lt;/p&gt;

&lt;p&gt;Thanks for reading, until the next post and as Dr Wallace Breen says in Half-Life 2..
Be wise. Be safe. Be aware!&lt;/p&gt;
</description>
        <pubDate>Thu, 26 Jun 2025 22:32:59 +0000</pubDate>
        <link>https://zroupas.github.io/automation/2025/06/26/dotfiles-management.html</link>
        <guid isPermaLink="true">https://zroupas.github.io/automation/2025/06/26/dotfiles-management.html</guid>
        
        <category>dotbot</category>
        
        <category>chezmoi</category>
        
        <category>dotfiles</category>
        
        <category>automation</category>
        
        <category>ansible</category>
        
        <category>git</category>
        
        <category>bash</category>
        
        
        <category>automation</category>
        
      </item>
    
      <item>
        <title>Track your personal tasks with the help of Docker and Kanboard!</title>
        <description>&lt;h3 id=&quot;track-your-personal-tasks-by-using-kanboard-and-nginx-proxy-docker-containers&quot;&gt;Track your personal tasks by using Kanboard and Nginx-Proxy docker containers.&lt;/h3&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;

&lt;p&gt;Hey everyone!&lt;/p&gt;

&lt;p&gt;If you are a person that cannot operate unless you write down to a paper or add a reminder or use one of the thousand different tools around the web to track your task, then you landed in the correct place!&lt;/p&gt;

&lt;p&gt;I’ve been struggling for years to keep my notes (and this is another project on it’s own) and personal tasks in one place.&lt;/p&gt;

&lt;p&gt;Some time ago, I stumbled upon an open source project called &lt;a href=&quot;https://kanboard.org/&quot; target=&quot;_blank&quot;&gt;Kanboard&lt;/a&gt; and I have been using this awesome tool for some months now in order to track different tasks , engineering or daily ones.&lt;/p&gt;

&lt;p&gt;So, let’s dive into the agile world!&lt;/p&gt;

&lt;h3 id=&quot;background&quot;&gt;Background&lt;/h3&gt;

&lt;p&gt;Kanboard is an Kanban open source project management software! You can see how it looks in Picture 1 below.&lt;/p&gt;

&lt;p&gt;Long story short, Kanban is an agile project management tool designed to help us visualize our tasks making it easier to establish an order, create a flow and most importantly understand the amount of work that we are planning to do!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/2023-10_kanboard-nginx/board.png&quot; data-lightbox=&quot;board&quot;&gt;
  &lt;img src=&quot;/assets/2023-10_kanboard-nginx/board.png&quot; title=&quot;board&quot; style=&quot;width:700px;&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;Picture 1&lt;/p&gt;

&lt;p&gt;If you are unfamiliar with terms like agile, scrum or kanban don’t be discouraged, you can start &lt;a href=&quot;https://en.wikipedia.org/wiki/Agile_software_development&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;concept&quot;&gt;Concept&lt;/h3&gt;

&lt;p&gt;As you may have seen already in one of my latest guides, on creating a MariaDB database host via docker found &lt;a href=&quot;https://myhomelab.gr/docker/2023/09/02/mysql-phpmyadmin.html&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt; , I wanted to have a single database that can host different projects. And one of them was Kanboard, so db wise, we are almost ready! 🚀&lt;/p&gt;

&lt;p&gt;While configuring Kanboard though and because other projects are pending in line, it seemed to me that having to access different services from different published ports would slow me down and add overhead trying to check or remember which port is connected with which service.&lt;/p&gt;

&lt;p&gt;So I thought that it would be better in the long run to have an nginx proxy to handle the requests and redirect them to appropriate services based on different ports. &lt;br /&gt; In that way I could also use my &lt;a href=&quot; http://jasonwilder.com/blog/2014/03/25/automated-nginx-reverse-proxy-for-docker/&quot; target=&quot;_blank&quot;&gt;local SSL Certificate Authority&lt;/a&gt; and enable SSL termination instead of plain http.&lt;/p&gt;

&lt;p&gt;You can check this &lt;a href=&quot;https://myhomelab.gr/linux/2019/12/13/local-ca-setup.html&quot; target=&quot;_blank&quot;&gt;great post &lt;/a&gt; if you have doubts on why it’s a good idea to use a reverse proxy infront of your containers!&lt;/p&gt;

&lt;h3 id=&quot;configuration&quot;&gt;Configuration&lt;/h3&gt;

&lt;p&gt;Following my networking setup decision described in my previous &lt;a href=&quot;https://myhomelab.gr/docker/2023/09/02/mysql-phpmyadmin.html&quot; target=&quot;_blank&quot;&gt;article&lt;/a&gt;, I decided to assign the two new images in my custom network called &lt;strong&gt;customnetwork&lt;/strong&gt;.&lt;/p&gt;

&lt;h3 id=&quot;nginx-proxy&quot;&gt;nginx-proxy&lt;/h3&gt;

&lt;p&gt;Now before running the command that will create our nginx-proxy container, need to prepare a bit the folders and files that nginx will need in order to redirect traffic to requested service based on the port.&lt;/p&gt;

&lt;p&gt;Below folder paths/names can be different and will be created on our host machine where docker is gonna be running:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; /docker_mounts/nginx-proxy/&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;html,conf.d,ssl,logs&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;For now I will mainly focus to folders that are necessary in this setup and especially to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;conf.d&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssl&lt;/code&gt;. The other two ones are optional and good to have but it depends on your need and setup.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;/docker_mounts/nginx-proxy/conf.d&lt;/strong&gt; : this folder will hold the custom configuration that is needed for the traffic redirection.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;/docker_mounts/nginx-proxy/ssl&lt;/strong&gt; : this folder will hold our custom ssl private authority files (have a look in &lt;a href=&quot;https://myhomelab.gr/linux/2019/12/13/local-ca-setup.html&quot; target=&quot;_blank&quot;&gt;this&lt;/a&gt; article to better understand or create your own local ssl private authority)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;nginx-proxy-customconf&quot;&gt;nginx-proxy custom.conf&lt;/h4&gt;

&lt;p&gt;Let’s copy our &lt;strong&gt;wildcard.homelab.home.*&lt;/strong&gt; private authority files to the newly &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssl&lt;/code&gt; created folder:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nb&quot;&gt;cp&lt;/span&gt; /tmp/wildcard.homelab.home.&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; /docker_mounts/nginx-proxy/ssl&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;For our custom config, copy/paste the below block to &lt;strong&gt;/docker_mounts/nginx-proxy/conf.d/custom.conf&lt;/strong&gt;&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;server &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        listen 80&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return &lt;/span&gt;301 https://&lt;span class=&quot;nv&quot;&gt;$host$request_uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

server &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        listen 443 ssl&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        server_name nginx-proxy.homelab.home&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        root /usr/share/nginx/html&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        ssl_certificate /etc/nginx/ssl/wildcard.homelab.home.crt&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        ssl_certificate_key /etc/nginx/ssl/wildcard.homelab.home.key&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        access_log      /var/log/nginx/nginx-proxy.homelab.home_ssl_access.log&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        error_log       /var/log/nginx/nginx-proxy.homelab.home_ssl_error.log&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        ssl_session_cache  &lt;span class=&quot;nb&quot;&gt;builtin&lt;/span&gt;:1000  shared:SSL:50m&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        ssl_protocols  TLSv1 TLSv1.1 TLSv1.2&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        ssl_prefer_server_ciphers on&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

server &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        listen 443 ssl&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        server_name kanboard.homelab.home&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        ssl_certificate /etc/nginx/ssl/wildcard.homelab.home.crt&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        ssl_certificate_key /etc/nginx/ssl/wildcard.homelab.home.key&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        access_log      /var/log/nginx/kanboard.homelab.home_ssl_access.log&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        error_log       /var/log/nginx/kanboard.homelab.home_ssl_error.log&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        ssl_session_cache  &lt;span class=&quot;nb&quot;&gt;builtin&lt;/span&gt;:1000  shared:SSL:50m&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        ssl_protocols  TLSv1 TLSv1.1 TLSv1.2&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        ssl_prefer_server_ciphers on&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        location / &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
      	proxy_set_header        Host &lt;span class=&quot;nv&quot;&gt;$host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      	proxy_set_header        X-Real-IP &lt;span class=&quot;nv&quot;&gt;$remote_addr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      	proxy_set_header        X-Forwarded-For &lt;span class=&quot;nv&quot;&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      	proxy_set_header        X-Forwarded-Proto &lt;span class=&quot;nv&quot;&gt;$scheme&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      	proxy_pass              http://172.17.0.1:8081&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Some major points to understand in the file above is that&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;in the first line, we instruct our nginx that needs to redirect any traffic coming to port 80 , to port 443.&lt;br /&gt;&lt;/li&gt;
  &lt;li&gt;in the ssl related lines, I use my custom &lt;strong&gt;wildcard.homelab.home.*&lt;/strong&gt; private authority files which I have already copied to the folder that is going to be mounted between host and container&lt;/li&gt;
  &lt;li&gt;nginx-proxy.homelab.home server section, is only relevant to make sure that our config proxy is working as expected before moving on to kanboard configuration. Add below lines in &lt;strong&gt;/docker_mounts/nginx-proxy/html/index.html&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&amp;lt;h1&amp;gt;Hey there!&amp;lt;/h1&amp;gt;
&amp;lt;p&amp;gt;Here is my &lt;span class=&quot;nb&quot;&gt;test &lt;/span&gt;HTML file&amp;lt;/p&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;ul&gt;
  &lt;li&gt;the actual line that tells our nginx where to send the traffic is mainly this one: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;proxy_pass          	http://172.17.0.1:8081;&lt;/code&gt;&lt;br /&gt;
This will instruct nginx, to send traffic that comes to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kanboard.homelab.home&lt;/code&gt; host, to the the docker default bridge network IP and port 8081 (which for now doesn’t have any service behind it but will have when we get to the &lt;a href=&quot;#kanboard-docker-run&quot;&gt;Kanboard section&lt;/a&gt;! 😉&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, you may ask yourself why not use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localhost&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;172.17.0.1&lt;/code&gt; (hint: run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker network inspect bridge&lt;/code&gt; to find the bridge IP) and this is due to the way networking is implemented in Docker Desktop for Mac (yeap, I know.. I know.. ), and the lack of a docker0 interface on the host. In a linux environment, I would guess that this won’t be a problem and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localhost&lt;/code&gt; will do the trick!&lt;/p&gt;

&lt;p&gt;Last but not least, append these lines to your /etc/hosts file in order to be able to resolv the hosts locally.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;127.0.0.1	nginx-proxy.homelab.home
127.0.0.1	kanboard.homelab.home&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;We finally have everything ready for our nginx-container!&lt;/p&gt;

&lt;h4 id=&quot;nginx-proxy-docker-run&quot;&gt;nginx-proxy docker run&lt;/h4&gt;

&lt;p&gt;Putting things together, the below command will start an nginx-proxy container that will listen to port &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;80&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;443&lt;/code&gt;.&lt;br /&gt;
It will also mount the already created host folders to the container, providing ngxinx with the relevant files needed for our configuration.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;docker run &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; nginx-proxy &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--hostname&lt;/span&gt; nginx-proxy.homelan.home &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--dns-search&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;homelab.home. &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 80:80 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 443:443 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; /var/run/docker.sock:/tmp/docker.sock:ro &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; /docker_mounts/nginx-proxy/html:/usr/share/nginx/html &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; /docker_mounts/nginx-proxy/nginx.conf:/etc/nginx/nginx.conf &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; /docker_mounts/nginx-proxy/conf.d:/etc/nginx/conf.d &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; /docker_mounts/nginx-proxy/ssl:/etc/nginx/ssl &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; /docker_mounts/nginx-proxy/logs:/var/log/nginx &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;-itd&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--restart&lt;/span&gt; always &lt;span class=&quot;nt&quot;&gt;--network&lt;/span&gt; customnetwork &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
jwilder/nginx-proxy&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;If everything worked as expected, a new container should be running and the our test host &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nginx-proxy.homelab.home&lt;/code&gt; should be accessible and secure.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/2023-10_kanboard-nginx/nginx-proxy.jpg&quot; data-lightbox=&quot;nginx-proxy&quot;&gt;
  &lt;img src=&quot;/assets/2023-10_kanboard-nginx/nginx-proxy.jpg&quot; title=&quot;nginx-proxy&quot; style=&quot;width:400px;&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/2023-10_kanboard-nginx/nginx-proxy-ssl.jpg&quot; data-lightbox=&quot;nginx-proxy-ssl&quot;&gt;
  &lt;img src=&quot;/assets/2023-10_kanboard-nginx/nginx-proxy-ssl.jpg&quot; title=&quot;nginx-proxy-ssl&quot; style=&quot;width:400px;&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to do any changes to the custom.conf, for example adding a new server section for a new service, then edit the file locally and then run the below commands that will check the config for errors and reload nginx service.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;docker &lt;span class=&quot;nb&quot;&gt;exec &lt;/span&gt;nginx-proxy nginx &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt;
docker &lt;span class=&quot;nb&quot;&gt;exec &lt;/span&gt;nginx-proxy nginx &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; reload&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h4 id=&quot;troubleshooting&quot;&gt;Troubleshooting&lt;/h4&gt;
&lt;p&gt;In case the above configuration notes didn’t work for you, then some troubleshooting steps to pin point the issue would be:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;If nginx-proxy container exited with error , you can check the logs for the reason behind it : &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker logs &amp;lt;container_name&amp;gt;&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;If the container started correctly, then check the logs found in the mounted logs folder &lt;strong&gt;/docker_mounts/nginx-proxy/logs&lt;/strong&gt; and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nginx-proxy.homelab.home_ssl_error.log&lt;/code&gt; for errors.&lt;/li&gt;
  &lt;li&gt;Make sure that host can be resolved and the entries in your hosts file are correct based on your local setup.&lt;/li&gt;
  &lt;li&gt;If none of the above fixed the problem, then a number of issues can be the root cause. &lt;br /&gt;Go back and make sure that you followed the steps and have replaced correctly your private auth, folder paths and filenames.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;kanboard&quot;&gt;kanboard&lt;/h3&gt;

&lt;p&gt;Now that we have our nginx-proxy container up and running, we need a service running in port 8081 and this is where kanboard comes into play!&lt;/p&gt;

&lt;h4 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h4&gt;

&lt;p&gt;Kanboard needs a database, so we will use our already configured MariaDB host in order to create the database and user that is going to be used in the docker run command.&lt;/p&gt;

&lt;p&gt;In order to connect to the MariaDB docker host, you can use either phpmyadmin GUI  or CLI commands both of which choices can be found in my previous &lt;a href=&quot;https://myhomelab.gr/docker/2023/09/02/mysql-phpmyadmin.html&quot; target=&quot;_blank&quot;&gt;article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After you have successfully connected, run below commands that will create a new database, a new user with a password and will give user full access to the specific database.  :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;CREATE DATABASE kanboard&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
CREATE USER &lt;span class=&quot;s1&quot;&gt;&apos;&amp;lt;KANBOARD_USER&amp;gt;&apos;&lt;/span&gt;@&lt;span class=&quot;s1&quot;&gt;&apos;localhost&apos;&lt;/span&gt; IDENTIFIED BY &lt;span class=&quot;s1&quot;&gt;&apos;&amp;lt;KANBOARD_PASSWD&amp;gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
CREATE USER &lt;span class=&quot;s1&quot;&gt;&apos;&amp;lt;KANBOARD_USER&amp;gt;&apos;&lt;/span&gt;@&lt;span class=&quot;s1&quot;&gt;&apos;%&apos;&lt;/span&gt; IDENTIFIED BY &lt;span class=&quot;s1&quot;&gt;&apos;&amp;lt;KANBOARD_PASSWD&amp;gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
GRANT ALL ON kanboard.&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; TO &lt;span class=&quot;s1&quot;&gt;&apos;&amp;lt;KANBOARD_USER&amp;gt;&apos;&lt;/span&gt;@&lt;span class=&quot;s1&quot;&gt;&apos;localhost&apos;&lt;/span&gt; WITH GRANT OPTION&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
GRANT ALL ON kanboard.&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; TO &lt;span class=&quot;s1&quot;&gt;&apos;&amp;lt;KANBOARD_USER&amp;gt;&apos;&lt;/span&gt;@&lt;span class=&quot;s1&quot;&gt;&apos;%&apos;&lt;/span&gt; WITH GRANT OPTION&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
FLUSH PRIVILEGES&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;strong&gt;Hint&lt;/strong&gt;: Make sure that you can login with the newly created user and credentials before starting kanboard container, this will save you a lot of troubleshooting time!&lt;/p&gt;

&lt;p&gt;It is also a good idea to have our data accessible directly in a host mounted folder so go ahead and create some basic ones (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kanboard_ssl&lt;/code&gt; isn’t needed in this case as we are now offloading SSL with the help of our ngxinx-proxy container).&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; /docker_mounts/kanboard/&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;kanboard_data,kanboard_plugins,kanboard_ssl&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h4 id=&quot;kanboard-docker-run&quot;&gt;kanboard docker run&lt;/h4&gt;

&lt;p&gt;Now that we have confirmed that the new database is accessible, let’s run the kanboard docker container.&lt;/p&gt;

&lt;p&gt;As you can see below:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;we expose port 8081 that will be used by the nginx-proxy to understand where to send traffic when &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kanboard.homelab.home&lt;/code&gt; is requested&lt;/li&gt;
  &lt;li&gt;we mount the needed folders&lt;/li&gt;
  &lt;li&gt;we use an environmental varialble in order for kanboard application to be able to connect to the database host, in our case &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mariadb&lt;/code&gt;. Replace variables below with the username and password configured &lt;a href=&quot;#prerequisites&quot;&gt;here&lt;/a&gt;!&lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;docker run &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; kanboard &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--hostname&lt;/span&gt; kanboard.homelab.home &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 8081:80 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--volume&lt;/span&gt; /docker_mounts/kanboard/kanboard_data:/var/www/app/data &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--volume&lt;/span&gt; /docker_mounts/kanboard/kanboard_plugins:/var/www/app/plugins &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--volume&lt;/span&gt; /docker_mounts/kanboard/kanboard_ssl:/etc/nginx/ssl &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;DATABASE_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;mysql://&amp;lt;KANBOARD_USER&amp;gt;:&amp;lt;KANBOARD_PASSWD&amp;gt;@mariadb/kanboard &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--restart&lt;/span&gt; always &lt;span class=&quot;nt&quot;&gt;--network&lt;/span&gt; customnetwork  &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
kanboard/kanboard&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;We are ready to run the docker command that will create our kanboard container.&lt;/p&gt;

&lt;p&gt;If no errors occur, then our we should have 3 containers successfully running!&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;e4904b212567   kanboard/kanboard         &lt;span class=&quot;s2&quot;&gt;&quot;/usr/local/bin/entr…&quot;&lt;/span&gt;   8 minutes ago   Up 7 minutes   443/tcp, 0.0.0.0:8081-&amp;gt;80/tcp                                                                        kanboard
401b545ab0a8   jwilder/nginx-proxy       &lt;span class=&quot;s2&quot;&gt;&quot;/app/docker-entrypo…&quot;&lt;/span&gt;   4 minutes ago   Up 3 minutes   0.0.0.0:80-&amp;gt;80/tcp, 0.0.0.0:443-&amp;gt;443/tcp                                                             nginx-proxy
ceac6732a896   mariadb:latest            &lt;span class=&quot;s2&quot;&gt;&quot;docker-entrypoint.s…&quot;&lt;/span&gt;   4 weeks ago   Up 8 hours   0.0.0.0:61115-&amp;gt;3306/tcp, 0.0.0.0:61116-&amp;gt;4444/tcp, 0.0.0.0:61117-&amp;gt;4567/tcp, 0.0.0.0:61118-&amp;gt;4568/tcp   mariadb&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Open a browser and head to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://kanboard.homelab.home&lt;/code&gt; , if all the above configuration is correct then we should be able to see our Kanboard login page!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/2023-10_kanboard-nginx/kanboard.jpg&quot; data-lightbox=&quot;kanboard&quot;&gt;
  &lt;img src=&quot;/assets/2023-10_kanboard-nginx/kanboard.jpg&quot; title=&quot;kanboard&quot; style=&quot;width:700px;&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hint&lt;/strong&gt;: The default login and password is admin/admin.&lt;/p&gt;

&lt;p&gt;There are many guides and videos on how to get started, so Happy Kanboarding! 🥳&lt;/p&gt;

&lt;h3 id=&quot;final-words-&quot;&gt;Final Words 💡&lt;/h3&gt;

&lt;p&gt;In this article we saw not only how to configure and start a kanboard container to create projects and track your tasks but also combined it with an nginx reverse proxy that can be used to server more services and projects which are pending in line!&lt;/p&gt;

&lt;p&gt;With the use of nginx, we can now choose a port, add a new server block in the custom.conf, as described &lt;a href=&quot;#nginx-proxy&quot;&gt;here&lt;/a&gt;, add a hostname or IP to your hosts file and boom, we can access our service with an FQDN which is much easier to remember especially if you don’t use a service daily.&lt;/p&gt;

&lt;p&gt;Yes, there is some overhead updating custom.conf and hosts file, maybe at some point I will have to also create a dns container,  but on the other hand as services are increasing, running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localhost:&amp;lt;port&amp;gt;&lt;/code&gt; isn’t so charming!&lt;/p&gt;

&lt;p&gt;Thanks for reading, until the next post and as Dr Wallace Breen says in Half-Life 2.. &lt;br /&gt;Be wise. Be safe. Be aware!&lt;/p&gt;
</description>
        <pubDate>Mon, 02 Oct 2023 22:32:59 +0000</pubDate>
        <link>https://zroupas.github.io/docker/2023/10/02/nginx-kanboard.html</link>
        <guid isPermaLink="true">https://zroupas.github.io/docker/2023/10/02/nginx-kanboard.html</guid>
        
        <category>docker</category>
        
        <category>containers</category>
        
        <category>images</category>
        
        <category>nginx-proxy</category>
        
        <category>reverse-proxy</category>
        
        <category>proxy</category>
        
        <category>kanboard</category>
        
        
        <category>docker</category>
        
      </item>
    
      <item>
        <title>Create MariaDB and phpMyAdmin containers locally via Docker</title>
        <description>&lt;h3 id=&quot;create-mariadb-and-phpmyadmin-containers-locally-via-docker&quot;&gt;Create MariaDB and phpMyAdmin containers locally via Docker.&lt;/h3&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;

&lt;p&gt;Long time no see, right! 😅&lt;/p&gt;

&lt;p&gt;Some time ago, and when I say some time ago I mean for more than 2 years now, I decided to stop creating VM’s in my local &lt;a href=&quot;https://myhomelab.gr/linux/virtualization/automation/2021/09/05/ansible_ovirt_api_part1.html&quot;&gt;oVirt Manager&lt;/a&gt; for each service that I wanted to experiment on and started using docker containers. Between us, I tried vagrant for a bit but this wasn’t my cup of tee so I decided to use Docker.&lt;/p&gt;

&lt;p&gt;I assume that whatever reason brought you to this article, you already know your way around docker containers but nevertheless I will try to explain as much as possible what this article is about and provide some major key points which are useful not only for the specific project but for docker handling in general.&lt;/p&gt;

&lt;p&gt;As with all my posts, the main reason for this one is to keep some personal notes on what I did locally in order to make everything work based on my own need of having a central local db that will host multiple databases from different projects. Of course, as different people are reaching out to answer their questions or ask for support , hopefully this will be also helpful for other people out there.&lt;/p&gt;

&lt;p&gt;In case you don’t have enough knowledge around docker and everything sounds like gibberish, please head to the &lt;a href=&quot;https://docs.docker.com/get-started/&quot;&gt;docker starting page&lt;/a&gt; which is a great starting point.&lt;/p&gt;

&lt;h3 id=&quot;concept&quot;&gt;Concept&lt;/h3&gt;

&lt;p&gt;As already described, the initial idea was that I needed a main database server but didn’t want to have a VM running all the time. There are multiple articles and maybe I can also create one at some point on how I configured my own Galera Cluster in Docker but for now we will only focus to a single server.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/2023-09_mysql-phpmyadmin/database_connections.png&quot; data-lightbox=&quot;database_connections.drawio&quot;&gt;
  &lt;img src=&quot;/assets/2023-09_mysql-phpmyadmin/database_connections.png&quot; title=&quot;database_connections.drawio&quot; style=&quot;width:700px;&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One database container to rule them all! 😈&lt;/p&gt;

&lt;h3 id=&quot;configuration&quot;&gt;Configuration&lt;/h3&gt;

&lt;h4 id=&quot;networking-prerequisite&quot;&gt;Networking prerequisite&lt;/h4&gt;

&lt;p&gt;After some &lt;a href=&quot;https://docs.docker.com/network/&quot;&gt;network related reading&lt;/a&gt;, some googling and multiple container deletions 😅, I decided to create a specific docker network so as &lt;strong&gt;1)&lt;/strong&gt; to be able to isolate specific docker containers and &lt;strong&gt;2)&lt;/strong&gt; because I had in mind that probably I will need an nginx-proxy and having their own network would save me from some networking trouble, but this is going to be part of my next article.&lt;/p&gt;

&lt;p&gt;In order to create a new docker network, run:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;docker network create customnetwork&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;If we want to check whether the new network is successfully created or which other local networks are available in our host, run:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;docker network &lt;span class=&quot;nb&quot;&gt;ls

&lt;/span&gt;4d59c36a3et5   bridge              bridge    &lt;span class=&quot;nb&quot;&gt;local
&lt;/span&gt;47ee586abggv   host                host      &lt;span class=&quot;nb&quot;&gt;local
&lt;/span&gt;f0a26628c86c   customnetwork       bridge    &lt;span class=&quot;nb&quot;&gt;local&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h4 id=&quot;mariadb-container&quot;&gt;MariaDB Container&lt;/h4&gt;

&lt;p&gt;Now let’s create the configuration for our custom &lt;a href=&quot;https://hub.docker.com/_/mariadb&quot;&gt;MariaDB&lt;/a&gt; container:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;docker run &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; mariadb &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;--hostname&lt;/span&gt; mariadb.homelab.prv &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;--publish&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;3306&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;--publish&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;4444&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;--publish&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;4567&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;--publish&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;4568&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;--dns-search&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;homelab.prv. &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;--env&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;MARIADB_ROOT_PASSWORD&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;somepassword&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;--env&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;MARIADB_USER&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;example-ruser&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;--env&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;MARIADB_PASSWORD&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;example-password&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;--volume&lt;/span&gt; /docker_mounts/mariadb/datadir:/var/lib/mysql &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;--volume&lt;/span&gt; /docker_mounts/mariadb/conf.d:/etc/mysql/mariadb.conf.d &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;--network&lt;/span&gt; customnetwork &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;--restart&lt;/span&gt; always &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
library/mariadb:latest&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Before running the above, let’s see what we are doing in each option:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;--name&lt;/strong&gt; : we override defult container’s name with a custom one,&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;--hostname&lt;/strong&gt; : we override the default hostname with our own one,&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;--publish&lt;/strong&gt; : we expose specific ports to the host so as to be reachable outside our container,&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;--dns-search&lt;/strong&gt; : we use DNS search domain to search non-fully-qualified hostnames,&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;--env&lt;/strong&gt; : next comes the environment variables for root password and an custom user,&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;--volume&lt;/strong&gt; : we mount mariadb datadir and conf dir to a specific path that we keep our mounted files,&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;--network&lt;/strong&gt; : we connect our container to our custom created network,&lt;/li&gt;
  &lt;li&gt;the last parameter is the image used for our container which is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mariadb&lt;/code&gt; latest one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When we run the above command, docker will check if the image &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mariadb:latest&lt;/code&gt; is present on our host else it will pull the related layers and then create the container with the given option.&lt;/p&gt;

&lt;p&gt;If no errors occured, we should see something like&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;docker ps
ceac6320a732   mariadb:latest        &lt;span class=&quot;s2&quot;&gt;&quot;docker-entrypoint.s…&quot;&lt;/span&gt;   8 seconds ago   Up 7 seconds   0.0.0.0:56158-&amp;gt;3306/tcp, 0.0.0.0:56159-&amp;gt;4444/tcp, 0.0.0.0:56156-&amp;gt;4567/tcp, 0.0.0.0:56157-&amp;gt;4568/tcp   mariadb&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;We can also follow (-f) and check the container logs with:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;docker logs mariadb &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now that the container is up, we should be able to login to our newly created MariaDB docker container with multiple ways, either by creating a new maridb container that runs the mariadb command line client against our original mariadb container, allowing us to execute SQL statements against our database instance:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;docker run &lt;span class=&quot;nt&quot;&gt;-it&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--network&lt;/span&gt; customnetwork &lt;span class=&quot;nt&quot;&gt;--rm&lt;/span&gt; mariadb mariadb &lt;span class=&quot;nt&quot;&gt;-hmariadb&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;or by opening a shell to the existing container which will also allow us to run mariadb commands&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;docker &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-it&lt;/span&gt; mariadb /bin/bash&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;root@mariadb:/# mariadb &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt;
Enter password:

Your MariaDB connection &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;is 6
Server version: 11.0.2-MariaDB-1:11.0.2+maria~ubu2204 mariadb.org binary distribution

Copyright &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;c&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type &lt;span class=&quot;s1&quot;&gt;&apos;help;&apos;&lt;/span&gt; or &lt;span class=&quot;s1&quot;&gt;&apos;\h&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;help. Type &lt;span class=&quot;s1&quot;&gt;&apos;\c&apos;&lt;/span&gt; to clear the current input statement.

MariaDB &lt;span class=&quot;o&quot;&gt;[(&lt;/span&gt;none&lt;span class=&quot;o&quot;&gt;)]&amp;gt;&lt;/span&gt; show databases&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
4 rows &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;0.024 sec&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Great, our new host is successfully created and accessible!&lt;/p&gt;

&lt;h4 id=&quot;phpmyadmin-container&quot;&gt;phpMyAdmin Container&lt;/h4&gt;

&lt;p&gt;Even though cli clients are awesome, software or web based clients are also a great option and oftenly even preferred by technical and non-technical people when it comes to more advanced database tasks.&lt;br /&gt; For that reason and because I wanted to make sure that my docker containers can communicate with each other , I decided to create a phpMyAdmin container and connect it to my existing mariadb host.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://hub.docker.com/_/phpmyadmin&quot;&gt;phpmyadmin&lt;/a&gt; isn’t so compicated to run as you can see from the relevant command below and one of the reasons is because it doesn’t need a specific mount point as it doesn’t actually hold any data or config that you may need to change out of the box.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;docker run &lt;span class=&quot;nt&quot;&gt;--restart&lt;/span&gt; always &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; phpmyadmin &lt;span class=&quot;nt&quot;&gt;--hostname&lt;/span&gt; phpmyadmin.homelab.prv &lt;span class=&quot;nt&quot;&gt;--dns-search&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;homelab.prv. &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 8081:80 &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;PMA_HOST&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;mariadb &lt;span class=&quot;nt&quot;&gt;--network&lt;/span&gt; customnetwork phpmyadmin&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Before running the above command, let’s see again what we are doing in each option apart from the already known ones that we already discussed in &lt;a href=&quot;#mariadb-container&quot;&gt;MariaDB&lt;/a&gt; section:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;-p 8081:80&lt;/strong&gt; : equivalent to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--publish&lt;/code&gt; , we expose container’s port 80 to our host 8081 port so as to be able to access the GUI, after the container is up and running. We will see how to do that in the next section.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;-e PMA_HOST=mariadb&lt;/strong&gt; : equivalent to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--env&lt;/code&gt; , we use &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PMA_HOST&lt;/code&gt;&lt;/strong&gt; variable to define the address or the host name of our MariaDB server. &lt;br /&gt;If you want to use phpMyAdmin to access multiple databases , then you will need also &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PMA_ARBITRARY=1&lt;/code&gt;&lt;/strong&gt; variable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s check docker status after running the latest command.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;docker ps
ceac6320a732   mariadb:latest        &lt;span class=&quot;s2&quot;&gt;&quot;docker-entrypoint.s…&quot;&lt;/span&gt;   2 hours ago   Up 2 hours   0.0.0.0:56158-&amp;gt;3306/tcp, 0.0.0.0:56159-&amp;gt;4444/tcp, 0.0.0.0:56156-&amp;gt;4567/tcp, 0.0.0.0:56157-&amp;gt;4568/tcp   mariadb
24304fe258f2   phpmyadmin                &lt;span class=&quot;s2&quot;&gt;&quot;/docker-entrypoint.…&quot;&lt;/span&gt;   5 seconds ago   Up 3 seconds   0.0.0.0:8081-&amp;gt;80 tcp   phpmyadmin&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Both containers are up and running! 🥳&lt;/p&gt;

&lt;h4 id=&quot;access-our-db-via-gui&quot;&gt;Access our DB via GUI&lt;/h4&gt;

&lt;p&gt;Now it’s time to open phpMYAdmin GUI and try to connect to our MariaDB.&lt;/p&gt;

&lt;p&gt;Open your favourite browser and type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://localhost:8081&lt;/code&gt; as shown in the image below and you should be able to at least see the phpMyAdmin welcome message!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/2023-09_mysql-phpmyadmin/phpmyadmin-gui.jpg&quot; data-lightbox=&quot;phpmyadmin-gui&quot;&gt;
  &lt;img src=&quot;/assets/2023-09_mysql-phpmyadmin/phpmyadmin-gui.jpg&quot; title=&quot;phpmyadmin-gui&quot; style=&quot;width:600px;&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Final and most important part of the whole setup is to be able to login to our MariaDB host via GUI, as we did via terminal already in a previous step, and be able to see and created new databases and any other related config via GUI.&lt;/p&gt;

&lt;p&gt;Use either &lt;strong&gt;root&lt;/strong&gt; user and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MARIADB_ROOT_PASSWORD&lt;/code&gt; or credentials of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MARIADB_USER&lt;/code&gt; configured &lt;a href=&quot;#mariadb-container&quot;&gt;here&lt;/a&gt;. &lt;br /&gt;If no errors occur, then you should see something like:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/2023-09_mysql-phpmyadmin/phpmyadmin-databases.jpg&quot; data-lightbox=&quot;phpmyadmin-databases&quot;&gt;
  &lt;img src=&quot;/assets/2023-09_mysql-phpmyadmin/phpmyadmin-databases.jpg&quot; title=&quot;phpmyadmin-databases&quot; style=&quot;width:600px;&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Awesome! 🥳&lt;/p&gt;

&lt;h3 id=&quot;final-words-&quot;&gt;Final Words 💡&lt;/h3&gt;

&lt;p&gt;You may have spotted already in the last screenshot, that among other databases I have one called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kanboard&lt;/code&gt; and one called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;phpipam&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is because of the next step and the actual reason behind this idea as I want to keep my tasks tracked in a &lt;strong&gt;Kanban Board&lt;/strong&gt; and this will be achieved with this amazing free open source &lt;a href=&quot;https://kanboard.org/&quot;&gt;Kanban project management software&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let me point out that every configuration described in this article, can be packed into a file which can be applied via docker compose but this is not the time to go through it and this is where the other database &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;phpipam&lt;/code&gt; comes into play! So stay tuned!&lt;/p&gt;

&lt;p&gt;Thanks for reading, until the next post and as Dr Wallace Breen says in Half-Life 2.. &lt;br /&gt;Be wise. Be safe. Be aware!&lt;/p&gt;

</description>
        <pubDate>Sat, 02 Sep 2023 09:32:59 +0000</pubDate>
        <link>https://zroupas.github.io/docker/2023/09/02/mysql-phpmyadmin.html</link>
        <guid isPermaLink="true">https://zroupas.github.io/docker/2023/09/02/mysql-phpmyadmin.html</guid>
        
        <category>docker</category>
        
        <category>containers</category>
        
        <category>images</category>
        
        <category>phpmyadmin</category>
        
        <category>mariadb</category>
        
        
        <category>docker</category>
        
      </item>
    
      <item>
        <title>Amateur Stop Motion Short Film - Klouvi</title>
        <description>&lt;h3 id=&quot;this-is-how-i-proposed-to-my-girlfriend&quot;&gt;This is how I proposed to my girlfriend!&lt;/h3&gt;

&lt;hr /&gt;

&lt;p&gt;Welcome to the first post of 2022!! Yes yes I know it’s November.. but it’s never too late, right? 😂&lt;/p&gt;

&lt;p&gt;I’ve been trying to write something for months now but kept delaying it for some unknown, to me, reason! So I decided to write about a project that I did back in 2015 which was a stop motion short film that I would then use to propose to my girlfriend!&lt;/p&gt;

&lt;p&gt;But before moving on, let me give some background before explaining how I combined my engineering and artistic skills!&lt;/p&gt;

&lt;h3 id=&quot;background-&quot;&gt;Background 📝&lt;/h3&gt;

&lt;p&gt;Not sure if this was genuinely my idea or if my girlfriend spend time whispering “you are now reaaaaady to propose to meeeeeee” in my ear during sleep but in any case I decided that it was time for me to do the next step after being together for 3 years.&lt;/p&gt;

&lt;p&gt;I’ve always been a fan of stop motion films/animations. From Jan Svankmajer and Quay Brothers to Tim Burton and Wes Anderson, from a young age I was fascinated by the idea of giving life to figures. So it was clear to me that creating my own stop motion short film would be a very romantic and unique way to propose!&lt;/p&gt;

&lt;p&gt;What I needed now was a set, a scenario, the appropriate music track and the actors!&lt;/p&gt;

&lt;h4 id=&quot;the-set&quot;&gt;The Set&lt;/h4&gt;

&lt;p&gt;Our first kiss took place in my favorite bar called Klouvi which is located in the heart of Athens, Greece in a neighborhood called Petralona! So I needed to recreate the bar which seemed duable and all I needed was some photos, some foam boards for the furniture and some miniatures to equip my fake bar!&lt;/p&gt;

&lt;h4 id=&quot;the-actors&quot;&gt;The Actors&lt;/h4&gt;

&lt;p&gt;While being an Erasmus student in Czech Republic back in 2005, I visited Prague where in one of my usual walks I found an awesome little toy store that had these 2 cute finger puppets, an old man and a old woman!&lt;/p&gt;

&lt;p&gt;One thing that someone needs to know about me, is that I tend to buy stuff just because I think that will be used at some point in the future. And how correct was I after 10 years, I had my actors!&lt;/p&gt;

&lt;p&gt;My future wife and myself, as an old couple going back to the place in which we had our first kiss! The scenario was almost ready!&lt;/p&gt;

&lt;h4 id=&quot;the-scenario&quot;&gt;The Scenario&lt;/h4&gt;

&lt;p&gt;Everything starts at a bar before it’s opening, we see a broom and a mop that are cleaning the floor, then chairs that start taking their place and glasses and cups are parading on the bar!&lt;/p&gt;

&lt;p&gt;When everything is ready, our two protagonists enter the bar and sit on the same place that sat years ago. They order a wine, they kiss and something suddenly grabs their attention..&lt;/p&gt;

&lt;h4 id=&quot;the-music&quot;&gt;The Music&lt;/h4&gt;

&lt;p&gt;For the music track I decided to use a song called &lt;a href=&quot;https://www.youtube.com/watch?v=Jz_Eu_je-S0&quot;&gt;“Tin Can Parade”&lt;/a&gt; from a great Greek band called &lt;a href=&quot;https://www.facebook.com/yourhandinmineband/&quot;&gt;“Your Hand In Mine”&lt;/a&gt; for two reasons. Firstly because they share the same name with my favorite song from a post-rock band called &lt;a href=&quot;https://www.youtube.com/watch?v=JzIK5FaC38w&quot;&gt;“Explosions In The Sky”&lt;/a&gt; and secondly because they are one of my favorite Greek bands and I really pictured me and my wife holding hands for a lot of years when I heard that particular song!&lt;/p&gt;

&lt;p&gt;Everything was ready for me to start building the bar and start shooting! 🎥&lt;/p&gt;

&lt;h3 id=&quot;building-the-sets-&quot;&gt;Building the Sets 🧰&lt;/h3&gt;

&lt;p&gt;I started small by creating a couple of chairs to make sure that the scale is correct. Didn’t want to have to deal with any complaints from my protagonists, at least not from the beginning!&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;img src=&quot;/assets/2022-11-20-marriage-proposal/chairs.jpg&quot; alt=&quot;&quot; /&gt;&lt;/td&gt;
      &lt;td&gt;&lt;img src=&quot;/assets/2022-11-20-marriage-proposal/sitting.jpg&quot; alt=&quot;&quot; /&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Not bad with just one try, right? So I decided to move on by creating some more chairs, tables and the bar related furniture.&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;img src=&quot;/assets/2022-11-20-marriage-proposal/chairs2.jpg&quot; alt=&quot;&quot; /&gt;&lt;/td&gt;
      &lt;td&gt;&lt;img src=&quot;/assets/2022-11-20-marriage-proposal/table.jpg&quot; alt=&quot;&quot; /&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;As it didn’t make sense to create my own equipment , I decided to order some miniature glasses, water and wine bottles from Ebay and luckily also the scale was spot on!&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;img src=&quot;/assets/2022-11-20-marriage-proposal/wines.jpg&quot; alt=&quot;&quot; /&gt;&lt;/td&gt;
      &lt;td&gt;&lt;img src=&quot;/assets/2022-11-20-marriage-proposal/painted1.jpg&quot; alt=&quot;&quot; /&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Next I saw that the original Klouvi bar had an espresso machine but couldn’t find one that was at the same size, scale wise, with the rest of the furniture.. so I created my own!&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;img src=&quot;/assets/2022-11-20-marriage-proposal/espresso1.jpg&quot; alt=&quot;&quot; /&gt;&lt;/td&gt;
      &lt;td&gt;&lt;img src=&quot;/assets/2022-11-20-marriage-proposal/espresso2.jpg&quot; alt=&quot;&quot; /&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The main bar was almost ready!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/2022-11-20-marriage-proposal/bar2.jpg&quot; data-lightbox=&quot;bar2&quot;&gt;
  &lt;img src=&quot;/assets/2022-11-20-marriage-proposal/bar2.jpg&quot; title=&quot;bar2&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then I started enriching it with the wine bottles and glasses that I had bought. I also added mops, grooms and a bucket with water needed for the first part of the scenario, the cleaning!&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;img src=&quot;/assets/2022-11-20-marriage-proposal/bar.jpg&quot; alt=&quot;&quot; /&gt;&lt;/td&gt;
      &lt;td&gt;&lt;img src=&quot;/assets/2022-11-20-marriage-proposal/bar3.jpg&quot; alt=&quot;&quot; /&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Some final touches with a Wine Cooler with ice cubs made from silicon and straws made from colored paperclips (I had to be creative as production was a bit low on cash 😂 ).&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;img src=&quot;/assets/2022-11-20-marriage-proposal/winecooler.jpg&quot; alt=&quot;&quot; /&gt;&lt;/td&gt;
      &lt;td&gt;&lt;img src=&quot;/assets/2022-11-20-marriage-proposal/straw.jpg&quot; alt=&quot;&quot; /&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;At this point I had what seemed to me like a full set which was pretty close to the actual bar!&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;img src=&quot;/assets/2022-11-20-marriage-proposal/bar4.jpg&quot; alt=&quot;&quot; /&gt;&lt;/td&gt;
      &lt;td&gt;&lt;img src=&quot;/assets/2022-11-20-marriage-proposal/bar5.jpg&quot; alt=&quot;&quot; /&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;a href=&quot;/assets/2022-11-20-marriage-proposal/klouvi.jpg&quot; data-lightbox=&quot;klouvi&quot;&gt;
  &lt;img src=&quot;/assets/2022-11-20-marriage-proposal/klouvi.jpg&quot; title=&quot;klouvi&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/2022-11-20-marriage-proposal/bar5.jpg&quot; data-lightbox=&quot;bar5&quot;&gt;
  &lt;img src=&quot;/assets/2022-11-20-marriage-proposal/bar5.jpg&quot; title=&quot;bar5&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Everything was ready and shootings got the green light!&lt;/p&gt;

&lt;h3 id=&quot;camera-rolling-&quot;&gt;Camera rolling 🎬&lt;/h3&gt;

&lt;p&gt;As many films, mine faced its own difficulties with the main one being the constant moving! I had to take the sets out of the closet each evening to take some pictures and then hide it again. I was leaving alone and my girlfriend would visit and of course she couldn’t see what I was preparing!&lt;/p&gt;

&lt;p&gt;This meant that I had to recreate the scene, re arrange the bottles or glasses and sometimes fix broken furniture! The positive thing was that I at least had the time and space to finish this project as we weren’t still leaving together.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/2022-11-20-marriage-proposal/shooting.jpg&quot; data-lightbox=&quot;shooting&quot;&gt;
  &lt;img src=&quot;/assets/2022-11-20-marriage-proposal/shooting.jpg&quot; title=&quot;shooting&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/2022-11-20-marriage-proposal/shooting2.jpg&quot; data-lightbox=&quot;shooting2&quot;&gt;
  &lt;img src=&quot;/assets/2022-11-20-marriage-proposal/shooting2.jpg&quot; title=&quot;shooting2&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The whole procedure took around 9 months and after ~1500 photos, I had the material to start putting together my first stop motion short film!&lt;/p&gt;

&lt;h3 id=&quot;montage-️&quot;&gt;Montage 🎞️&lt;/h3&gt;

&lt;p&gt;Next step was to create a video out of the all the photos that I had gathered. This could be done with various ways but I will document the one that worked for me and keep in mind that I had already been working on this project for 9 months and Im was in the brink of giving up!&lt;/p&gt;

&lt;p&gt;The tools that I used in order to join all the photos in one video are the ones below!&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;dnf &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;pyrenamer
dnf &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;ffmpeg&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;First tool that I installed was &lt;a href=&quot;https://github.com/tfree87/pyRenamer&quot;&gt;pyRenamer&lt;/a&gt; which is a powerful application with a graphic interface designed for renaming many files at once. Yes I know what you are thinking, why not using awk, sed and other linux commands but keep in mind that at that point, I was already working on this project for more than 8 months and my time window was closing! 
Second tool was ffmpeg, which is a complete cross-platform solution to record, convert and stream audio and video.&lt;/p&gt;

&lt;p&gt;So pyRenamer did exactly what I wanted quickly and easily. The only thing that someone has to do, is choose the folder that keeps all the relevant photos and add the below pattern in the “Renamed file name pattern” field, check the Preview and hit Rename.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;frame&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;num6&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;.jpg&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/tfree87/pyRenamer/master/screenshots/screenshot.png&quot; alt=&quot;pyRenamer&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This was needed for the next tool to be able to join the photos in the correct order!&lt;/p&gt;

&lt;p&gt;Then ffmpeg comes into play to perform it’s magic! In case you have questions on the options used below, you can find some very useful examples &lt;a href=&quot;https://trac.ffmpeg.org/wiki/How%20to%20speed%20up%20/%20slow%20down%20a%20video&quot;&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;ffmpeg &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; frame%06d.jpg &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt;:v libx264 &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; 30 &lt;span class=&quot;nt&quot;&gt;-pix_fmt&lt;/span&gt; yuv420p klouvi_stop_motion.mp4
ffmpeg &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; klouvi_stop_motion.mp4 &lt;span class=&quot;nt&quot;&gt;-filter&lt;/span&gt;:v &lt;span class=&quot;s2&quot;&gt;&quot;setpts=7.0*PTS&quot;&lt;/span&gt; klouvi_stop_motion.mp4
ffmpeg &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; klouvi_stop_motion.mp4 &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; audio.ogg &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; copy &lt;span class=&quot;nt&quot;&gt;-map&lt;/span&gt; 0:v &lt;span class=&quot;nt&quot;&gt;-map&lt;/span&gt; 1:a klouvi_stop_motion.mkv&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;As you can see above, there are 3 commands that will create a video out of a bunch of photos.&lt;/p&gt;

&lt;p&gt;With the first command we are creating the initial klouvi_stop_motion.mp4 by joining the jpg files. With the second we are slowing down the video speed by changing the presentation timestamp (PTS) of each video frame. And finally with the third command, we are adding our audio.ogg to the finalized version of our short film!&lt;/p&gt;

&lt;p&gt;Voila! My first and most important stop motion short film was ready!&lt;/p&gt;

&lt;h3 id=&quot;final-words-&quot;&gt;Final Words 💡&lt;/h3&gt;

&lt;p&gt;This was a journey that lasted almost a year, had its ups and downs but the end result was very fulfilling as I got the chance to combine my creativity and my engineering skills in order to create a marriage proposal that was quite unique and totally me!&lt;/p&gt;

&lt;p&gt;Probably there were better ways to do the photoshoots and the editing than with a single One Plus X mobile (that I was using at the time), a basic camera stand and some ffmpeg commands but it totally worth the struggle!&lt;/p&gt;

&lt;p&gt;Thanks for reading, until the next post and as Dr Wallace Breen says in Half-Life 2.. &lt;br /&gt;Be wise. Be safe. Be aware!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PS: Let’s see what the future brings.. but for now I guess you are wondering whether my girlfriend said yes or no, so no need to keep you waiting any longer! Enjoy!&lt;/strong&gt;&lt;/p&gt;

&lt;iframe width=&quot;760&quot; height=&quot;515&quot; src=&quot;https://www.youtube.com/embed/Jgl4zaipy6o&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

</description>
        <pubDate>Sun, 20 Nov 2022 20:32:59 +0000</pubDate>
        <link>https://zroupas.github.io/hobbies/2022/11/20/marriage_proposal.html</link>
        <guid isPermaLink="true">https://zroupas.github.io/hobbies/2022/11/20/marriage_proposal.html</guid>
        
        <category>video,marriage,proposal,short</category>
        
        <category>film,stop</category>
        
        <category>motion</category>
        
        
        <category>hobbies</category>
        
      </item>
    
      <item>
        <title>Automatically create virtual machines via Ansible and oVirt API - Part Two</title>
        <description>&lt;h3 id=&quot;automatically-create-virtual-machines-via-ansible-and-ovirt-api---part-two&quot;&gt;Automatically create virtual machines via Ansible and oVirt API - Part Two.&lt;/h3&gt;

&lt;hr /&gt;

&lt;p&gt;Welcome to the second part of our oVirt API manipulation via Ansible guide!&lt;/p&gt;

&lt;p&gt;I guess you’ve already gone through the &lt;a href=&quot;https://myhomelab.gr/linux/virtualization/automation/2021/09/05/ansible_ovirt_api_part1.html&quot;&gt;first part&lt;/a&gt;, played around with &lt;strong&gt;Ansible&lt;/strong&gt; on your own and waited anxiously for the second one (Noooot). Or maybe you’ve already found a way to use Ansible and create your own VM’s automatically after cursing for losing time reading the first part which was useless in your case! :D&lt;/p&gt;

&lt;p&gt;But waiting time is over!!&lt;/p&gt;

&lt;h3 id=&quot;architecture-️&quot;&gt;Architecture ✏️&lt;/h3&gt;

&lt;p&gt;To get a reminder of what our goal is and what we are gonna try to achieve in this project, check below picture&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ansible-api/architecture.png&quot; data-lightbox=&quot;architecture&quot;&gt;
  &lt;img src=&quot;/assets/ansible-api/architecture.png&quot; title=&quot;architecture&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and for a more detailed explanation head to the relevant &lt;a href=&quot;https://myhomelab.gr/linux/virtualization/automation/2021/09/05/ansible_ovirt_api_part1.html&quot;&gt;post&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;playbooks-&quot;&gt;Playbooks 📚&lt;/h3&gt;

&lt;p&gt;In the first part we managed, as an example, to get a list of our active VM’s and their snapshot description. Now we will head directly to the interesting part without wasting any more time.&lt;/p&gt;

&lt;p&gt;I assume that you have basic understanding on how Ansible works by now, and if not head to the &lt;a href=&quot;https://myhomelab.gr/linux/virtualization/automation/2021/09/05/ansible_ovirt_api_part1.html&quot;&gt;first part&lt;/a&gt; to find useful informations and links.&lt;/p&gt;

&lt;p&gt;But just as a reminder in order to be able to connect to the remote host you need an inventory. This is an essential part of the initial configuration, otherwise you will end up with below output if you try to run your playbook:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;PLAY [10.0.0.11] **********************************************************************************************************************************************************************************************
skipping: no hosts matched
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;After making sure you have a correctly configured Ansible environment, we are ready to proceed.&lt;/p&gt;

&lt;h4 id=&quot;create-our-first-vm-via-ansible&quot;&gt;Create our first VM via Ansible&lt;/h4&gt;
&lt;p&gt;Open your favourite terminal and paste the command below so as to create the playbook &lt;strong&gt;ovirt_create_vm.yml&lt;/strong&gt; which will be used to connect to oVirt and automatically instruct oVirt to create our next VM for us !&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yml&quot; data-lang=&quot;yml&quot;&gt;&lt;span class=&quot;s&quot;&gt;:~$ cat &amp;lt;&amp;lt;EOF &amp;gt; ovirt_create_vm.yml&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;hosts&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ovirt.homelab.home&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;local&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;tasks&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Obtain SSO token&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ovirt_auth&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https://ovirt.homelab.home/ovirt-engine/api&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;admin@internal&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;lt;add&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;your&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;here&amp;gt;&quot;&lt;/span&gt;
      
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Create VM with cloud-init&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ovirt_vms&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apivm&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;lt;add&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;your&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;here&amp;gt;&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;cluster&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;lt;add&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;your&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;cluster&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;here&amp;gt;&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;memory&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;1GiB&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;auth&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ovirt_auth&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;}}&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;running&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;cloud_init&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;nic_boot_protocol&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;static&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;nic_ip_address&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;10.0.0.11&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;nic_netmask&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;255.255.255.0&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;nic_gateway&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;10.0.0.254&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;nic_name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;eth0&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;nic_on_boot&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;host_name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apivm&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;custom_script&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;write_files:&lt;/span&gt;
           &lt;span class=&quot;s&quot;&gt;- content: |&lt;/span&gt;
               &lt;span class=&quot;s&quot;&gt;Hello, world!&lt;/span&gt;
             &lt;span class=&quot;s&quot;&gt;path: /tmp/greeting.txt&lt;/span&gt;
             &lt;span class=&quot;s&quot;&gt;permissions: &apos;0644&apos;&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;runcmd:&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;- yum -y update&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;user_name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;root&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;root_password&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apivm&lt;/span&gt;

  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Always revoke the SSO token&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ovirt_auth&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;absent&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;ovirt_auth&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ovirt_auth&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;}}&quot;&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;EOF&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Easy right? I think it’s much more straight forward than the list VM’s playbook that we used in the part one .We are going to breakdown and explain each task as good as possible.&lt;/p&gt;

&lt;h4 id=&quot;playbook-breakdown&quot;&gt;Playbook breakdown&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;The first section is the &lt;em&gt;host&lt;/em&gt; which tells our playbook which is the target host (or group of hosts in other cases) that the playbook is going to be executed on. In our case the oVirt host of our infrastructure ,&lt;strong&gt;ovirt.homelab.home&lt;/strong&gt;, which provides the &lt;strong&gt;REST-API&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;Next we have the &lt;em&gt;tasks&lt;/em&gt; section and the first &lt;em&gt;play&lt;/em&gt; where we are using the &lt;strong&gt;ovirt_auth&lt;/strong&gt; module to authenticate to oVirt engine and create an &lt;strong&gt;SSO toke&lt;/strong&gt;n which will be used in the next play.&lt;/li&gt;
  &lt;li&gt;In this play is where the magic happens! By using the &lt;strong&gt;ovirt_vms&lt;/strong&gt; module we are configuring our new VM to have/use a:
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;name&lt;/strong&gt;: The name of our choosing, &lt;em&gt;apivm&lt;/em&gt; in our case&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;template&lt;/strong&gt;: A template as described in the previous parts. You can either use your own or use the ones provided by ovirt-image-repository found in &lt;strong&gt;Storage&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Storage Domains&lt;/strong&gt;.&lt;br /&gt;I use my own one which is already configured in a way that every new VM will have basic configuration to make it manageable from the Ansible VM, &lt;strong&gt;ansible.homelab.home&lt;/strong&gt; (ex. ansible ssh keys , ansible user, cloud-init service etc).&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;resources &amp;amp; state&lt;/strong&gt;: I’m only using 1GB of RAM and change the VM state to running.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;cloud-init&lt;/strong&gt;: This is the time to setup a static IP instead of letting your DHCP  assign a dynamic one and having to login via console each time to find out!&lt;br /&gt;We can do a lot more with cloud-init, in my example I also create a greetings.txt so for debugging reasons, I’m updating the OS so as to have the latest packages, not a great idea in a production environment, and finally changing the root credentials. And all of that with just one click! Cool right?&lt;/li&gt;
      &lt;li&gt;In the final task we use the &lt;strong&gt;ovirt_auth&lt;/strong&gt; module in order to revoke the created SSO token from the first play. This step is optional as the SSO will be revoked automatically after some days based on the default oVirt configuration but I always find it more secure to revoke it after I have finished my task as it is not needed any more.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s see what we have configured!&lt;/p&gt;

&lt;h4 id=&quot;execution-&quot;&gt;Execution 🐧&lt;/h4&gt;

&lt;h5 id=&quot;create-vm-&quot;&gt;Create VM ⏫&lt;/h5&gt;
&lt;p&gt;Before running our playbook let’s make sure that IP 10.0.010 isn’t already used by any other VM,&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;ping 10.0.0.11  &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; 5
PING 10.0.0.11 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;10.0.0.11&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 56&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;84&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; bytes of data.  
From 10.0.0.227 &lt;span class=&quot;nv&quot;&gt;icmp_seq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1 Destination Host Unreachable  
From 10.0.0.227 &lt;span class=&quot;nv&quot;&gt;icmp_seq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;2 Destination Host Unreachable  
From 10.0.0.227 &lt;span class=&quot;nv&quot;&gt;icmp_seq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;3 Destination Host Unreachable  
From 10.0.0.227 &lt;span class=&quot;nv&quot;&gt;icmp_seq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;4 Destination Host Unreachable  
From 10.0.0.227 &lt;span class=&quot;nv&quot;&gt;icmp_seq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;5 Destination Host Unreachable  
  
&lt;span class=&quot;nt&quot;&gt;---&lt;/span&gt; 10.0.0.11 ping statistics &lt;span class=&quot;nt&quot;&gt;---&lt;/span&gt;  
5 packets transmitted, 0 received, +14 errors, 100% packet loss, &lt;span class=&quot;nb&quot;&gt;time &lt;/span&gt;17414ms&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;It’s probably the first time that I got so much pleasure from 100% failure.. and we are good to go! Open your favourite terminal and run your creation playbook:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;ansible-playbook ovirt_create_vm.yml&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;and check the output:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;PLAY [ovirt.homelab.home] ********************************************************************************************************************************************************************************************  
  
TASK [Gathering Facts] ********************************************************************************************************************************************************************************************  
ok: [ovirt.homelab.home]  
  
TASK [Obtain SSO token] *******************************************************************************************************************************************************************************************  
ok: [ovirt.homelab.home]  
  
TASK [Run VM with cloud init] *************************************************************************************************************************************************************************************
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If everything is configured correctly you should be stuck in the “&lt;strong&gt;Run VM with cloud init&lt;/strong&gt;” task which for now looks promising! It means that something is at least happening in the background.&lt;br /&gt;Let’s connect to our oVirt GUI and see what is Ansible doing, login and head to the &lt;strong&gt;Events&lt;/strong&gt; section.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ansible-api-2/events_1.png&quot; data-lightbox=&quot;events_1&quot;&gt;
  &lt;img src=&quot;/assets/ansible-api-2/events_1.png&quot; title=&quot;events_1&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What a sight for sore eyes! &lt;strong&gt;oVirt&lt;/strong&gt; was instructed to create a new vm and as we can see above, and &lt;strong&gt;apivm&lt;/strong&gt; is now being prepared! We can check the progress in &lt;strong&gt;Compute&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Virtual Machines&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ansible-api-2/apivm-configure.png&quot; data-lightbox=&quot;apivm-configure&quot;&gt;
  &lt;img src=&quot;/assets/ansible-api-2/apivm-configure.png&quot; title=&quot;apivm-configure&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The VM has already started and we can successfully login via console!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ansible-api-2/apivm-started.png&quot; data-lightbox=&quot;apivm-started&quot;&gt;
  &lt;img src=&quot;/assets/ansible-api-2/apivm-started.png&quot; title=&quot;apivm-started&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ansible-api-2/console.png&quot; data-lightbox=&quot;console&quot;&gt;
  &lt;img src=&quot;/assets/ansible-api-2/console.png&quot; title=&quot;console&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The VM is added to our network and we are able to ping it and connect directly via SSH instead of having to check which IP would have been assigned to our VM via console.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;ping 10.0.0.11 &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; 5  
PING 10.0.0.11 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;10.0.0.11&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 56&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;84&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; bytes of data.  
64 bytes from 10.0.0.11: &lt;span class=&quot;nv&quot;&gt;icmp_seq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1 &lt;span class=&quot;nv&quot;&gt;ttl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;64 &lt;span class=&quot;nb&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0.575 ms  
64 bytes from 10.0.0.11: &lt;span class=&quot;nv&quot;&gt;icmp_seq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;2 &lt;span class=&quot;nv&quot;&gt;ttl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;64 &lt;span class=&quot;nb&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0.802 ms  
64 bytes from 10.0.0.11: &lt;span class=&quot;nv&quot;&gt;icmp_seq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;3 &lt;span class=&quot;nv&quot;&gt;ttl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;64 &lt;span class=&quot;nb&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0.869 ms  
64 bytes from 10.0.0.11: &lt;span class=&quot;nv&quot;&gt;icmp_seq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;4 &lt;span class=&quot;nv&quot;&gt;ttl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;64 &lt;span class=&quot;nb&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0.782 ms  
64 bytes from 10.0.0.11: &lt;span class=&quot;nv&quot;&gt;icmp_seq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;5 &lt;span class=&quot;nv&quot;&gt;ttl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;64 &lt;span class=&quot;nb&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0.874 ms  
  
&lt;span class=&quot;nt&quot;&gt;---&lt;/span&gt; 10.0.0.11 ping statistics &lt;span class=&quot;nt&quot;&gt;---&lt;/span&gt;  
5 packets transmitted, 5 received, 0% packet loss, &lt;span class=&quot;nb&quot;&gt;time &lt;/span&gt;4083ms &lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;In the meantime and after having created successfully the requested VM, the playbook continued by revoking the SSO token that we have created in the second task!&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;TASK [Always revoke the SSO token] ********************************************************************************************************************************************************************************  
ok: [ovirt.homelab.home]  
  
PLAY RECAP ********************************************************************************************************************************************************************************************************  
ovirt.homelab.home            : ok=4    changed=1    unreachable=0    failed=0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Playbook finished it’s job and the recap informs us that that all 4 tasks run successfully and 1 change was applied!!&lt;/p&gt;

&lt;p&gt;Strangely everything worked out exactly as planned!! 😂 🥳&lt;/p&gt;

&lt;h5 id=&quot;shutdown-vm-&quot;&gt;Shutdown VM ⏬&lt;/h5&gt;

&lt;p&gt;The next playbook is the one that will conclude the second part and we will be used to shutdown the VM that we created in the previous section.&lt;br /&gt;Open your favourite terminal and run below command that will create &lt;strong&gt;ovirt_stop_vm.yml&lt;/strong&gt; :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yml&quot; data-lang=&quot;yml&quot;&gt;&lt;span class=&quot;s&quot;&gt;:~$ cat &amp;lt;&amp;lt;EOF &amp;gt; ovirt_restart_vm.yml&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;hosts&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ovirt.homelab.home&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;local&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;tasks&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Obtain SSO token&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ovirt_auth&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https://ovirt.homelab.home/ovirt-engine/api&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;admin@internal&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;lt;add&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;your&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;here&amp;gt;&quot;&lt;/span&gt;

  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Stop vm&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ovirt_vms&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;auth&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ovirt_auth&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;}}&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apivm&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;stopped&lt;/span&gt;

  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Always revoke the SSO token&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ovirt_auth&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;absent&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;ovirt_auth&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ovirt_auth&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;}}&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;In addition with the previous ones , i can safely say that the above playbook is quite simple. The only point to keep in mind regarding the state handling above is that the &lt;strong&gt;ovirt_vms&lt;/strong&gt; module doesn’t support restart, at this point of time at least, and the available states are: &lt;em&gt;absent, next_run, present, registered, running, stopped, suspended&lt;/em&gt;. So in case you want to restart a VM you need one more task , with state: running!&lt;/p&gt;

&lt;p&gt;Let’s run it and put our new VM to sleep until the third part of this awsome series arrives! 😉&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;ansible-playbook ovirt_restart_vm.yml&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;PLAY [ovirt.homelab.home] ***************************************************************************************************************  
  
TASK [Gathering Facts] ***************************************************************************************************************  
ok: [ovirt.homelab.home]  
  
TASK [Obtain SSO token] **************************************************************************************************************  
ok: [ovirt.homelab.home]  
  
TASK [Stop vm] ***********************************************************************************************************************  
changed: [ovirt.homelab.home]  
  
TASK [Always revoke the SSO token] ***************************************************************************************************  
ok: [ovirt.homelab.home]  
  
PLAY RECAP ***************************************************************************************************************************  
ovirt.homelab.home            : ok=4    changed=1    unreachable=0    failed=0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;/assets/ansible-api-2/apivm-shutdown.png&quot; data-lightbox=&quot;apivm-shutdown&quot;&gt;
  &lt;img src=&quot;/assets/ansible-api-2/apivm-shutdown.png&quot; title=&quot;apivm-shutdown&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the successful run of this playbook, the actual task of manipulating the oVirt API in order to create our new VM via Ansible, is now completed! Head to your infrastructure and happy automating!!&lt;/p&gt;

&lt;h3 id=&quot;final-words-&quot;&gt;Final Words 💡&lt;/h3&gt;

&lt;p&gt;I hope that I managed to explain, or at least make a bit clearer, what is an API, and how to interact with it with the help of &lt;strong&gt;Ansible&lt;/strong&gt; automation tool.&lt;/p&gt;

&lt;p&gt;I think that both a beginner and an intermediate user can find very useful information’s for getting started or dig deeper to get the most out of the &lt;strong&gt;oVirt REST API&lt;/strong&gt; by extending the provided playbooks to create storage pools, new hosts, network profiles ,users and many more.. automatically!&lt;/p&gt;

&lt;p&gt;As Ansible is a huge chapter, I will use the momentum to stay a bit longer in the automation area and use the next post to experiment with tasks that can be run directly on the remote server and create playbooks that will act as a “single source of truth” in our infrastructure.&lt;/p&gt;

&lt;p&gt;We will see how to disable services (that I don’t like 😆) , copy our own configuration files, use a bash script to wrap everything around it and see how we can pass pass extra variables to our playbooks!&lt;/p&gt;

&lt;p&gt;Thanks for reading, until the next post and as Dr Wallace Breen says in Half-Life 2.. &lt;br /&gt;Be wise. Be safe. Be aware!&lt;/p&gt;

</description>
        <pubDate>Wed, 17 Nov 2021 20:32:59 +0000</pubDate>
        <link>https://zroupas.github.io/linux/virtualization/automation/2021/11/17/ansible_ovirt_api_part2.html</link>
        <guid isPermaLink="true">https://zroupas.github.io/linux/virtualization/automation/2021/11/17/ansible_ovirt_api_part2.html</guid>
        
        <category>linux,ovirt,api,ansible</category>
        
        
        <category>linux</category>
        
        <category>Virtualization</category>
        
        <category>automation</category>
        
      </item>
    
      <item>
        <title>Automatically create virtual machines via Ansible and oVirt API - Part One</title>
        <description>&lt;h3 id=&quot;automatically-create-virtual-machines-via-ansible-and-ovirt-api---part-one&quot;&gt;Automatically create virtual machines via Ansible and oVirt API - Part One.&lt;/h3&gt;

&lt;hr /&gt;

&lt;p&gt;At this point my (virtual) infrastructure was ready to welcome more virtual machines that could be successfully backed up and restored to a previous state. If you want to check the backup/restore procedure that I’m following, you can find all related info to the &lt;a href=&quot;https://myhomelab.gr/virtualization/2020/07/13/backup-virtual-machines.html&quot;&gt;Create and Export Full oVirt VM Backups&lt;/a&gt; post.&lt;/p&gt;

&lt;p&gt;So nothing could go wrong or get between me and my fancy ideas any more!&lt;/p&gt;

&lt;p&gt;But even though i was super excited and wanted to directly create as many virtual machines as I could, I didn’t like the idea of having to follow all the manual steps that I have documented in my previous posts for each and every new vm!&lt;/p&gt;

&lt;p&gt;Then it hit me! What if I could automate the procedure and create a new virtual machine with a push of a button? To do that I needed an automation tool that could make use of the &lt;strong&gt;oVirt REST-API&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It was time to bring out the big guns for my first official project! &lt;strong&gt;Ansible&lt;/strong&gt;!&lt;/p&gt;

&lt;h3 id=&quot;appendix-&quot;&gt;Appendix 📓&lt;/h3&gt;
&lt;p&gt;First things first though and before moving to the interesting part we need to explain the terms that are going to be used in this post.&lt;/p&gt;

&lt;h4 id=&quot;whats-an-api--rest-api&quot;&gt;What’s a(n) API / REST-API?&lt;/h4&gt;
&lt;p&gt;First let’s start by explaining what an &lt;strong&gt;API&lt;/strong&gt; is. I won’t get into much detail because there are tons of great material to get information from but for the purposes of this post I will document below the explanation that made sense to me when I first encountered this concept.&lt;/p&gt;

&lt;p&gt;Think of the API as the &lt;strong&gt;Waiter&lt;/strong&gt; in your favourite restaurant. He is the one standing between you (&lt;em&gt;Application/Client&lt;/em&gt;) and your delicious meal. Of course you have the menu to decide but let’s say you want the BBQ Burger 🍔 but with the tomato removed! You need to ask the &lt;strong&gt;Waiter&lt;/strong&gt; (&lt;em&gt;API&lt;/em&gt;) if this is possible, he will then have to forward your request to the &lt;strong&gt;Kitchen&lt;/strong&gt; (&lt;em&gt;Server&lt;/em&gt;) and return with an answer (and probably with a spit on your burger 😂 ).&lt;/p&gt;

&lt;p&gt;Now let’s get a bit more professional shall we? &lt;strong&gt;API&lt;/strong&gt;  (&lt;em&gt;Application Programming Interface&lt;/em&gt;) is a set of definitions and protocols for building application software that acts as an intermediary allowing two applications to talk to each other by granting all the required permissions.&lt;/p&gt;

&lt;p&gt;Wait, we are not done yet!! &lt;strong&gt;oVirt Engine&lt;/strong&gt; is providing a &lt;a href=&quot;https://www.ovirt.org/develop/api/rest-api/rest-api.html&quot;&gt;REST-API&lt;/a&gt;, a subset of &lt;strong&gt;APIs&lt;/strong&gt;, which is a set of guidelines for building a web service API which uses &lt;strong&gt;URIs&lt;/strong&gt; and &lt;strong&gt;HTTP&lt;/strong&gt; protocol and &lt;strong&gt;JSON&lt;/strong&gt; for data format. &lt;strong&gt;REST-APIs&lt;/strong&gt; are more flexible as they can handle different types of calls and return data in different formats (&lt;strong&gt;XML, JSON, YAML&lt;/strong&gt; etc).&lt;/p&gt;

&lt;h4 id=&quot;and-what-about-ansible&quot;&gt;And what about Ansible?&lt;/h4&gt;

&lt;p&gt;There are multiple automation tools out there and each one has it’s own strengths and weaknesses and let me explain what I mean! For example, I din’t want to mess with ruby at this point (or at any point of time honestly), so this ruled out &lt;del&gt;&lt;strong&gt;Chef&lt;/strong&gt;&lt;/del&gt; immediately. I also wanted an agentless push configuration solution which also ruled out &lt;del&gt;&lt;strong&gt;Puppet&lt;/strong&gt;&lt;/del&gt;. I didn’t have any experience with &lt;strong&gt;Salt&lt;/strong&gt;, so this left me with .. 🥁.. &lt;strong&gt;Ansible&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;I don’t have a fancy example this time to be honest, but I have an awesome &lt;em&gt;Fun Fact&lt;/em&gt; that you probably aren’t aware of!&lt;br /&gt;The term “ansible” was first introduced in 1966 Ursula K. Le Guin’s novel &lt;strong&gt;Rocannon’s World&lt;/strong&gt;!! As I am a huge Ursula Le Guin and Sc-fi fan, this was mind blowing for me back when I first came across to the &lt;strong&gt;Ansible&lt;/strong&gt; software.&lt;/p&gt;

&lt;p&gt;Ansible is the &lt;strong&gt;Swiss army knife&lt;/strong&gt; equivalent of IT automation engines, a powerful open-source automation tool that acts as a configuration management, application deployment, orchestrator and cloud provisioning.&lt;/p&gt;

&lt;p&gt;Ansible is an &lt;strong&gt;agentless tool&lt;/strong&gt; that runs in a ‘push’ model, temporarily connecting remotely via SSH or Windows Remote Management to make them manageable. This makes it the best option for our project as it is a flexible and highly customizable tool which provides an ad-hoc way to configure and manage various parts of our oVirt infrastructure.&lt;/p&gt;

&lt;p&gt;And the best part is that oVirt has everything ready for us as it is already maintaining multiple &lt;a href=&quot;https://github.com/oVirt/ovirt-ansible&quot;&gt;Ansible roles&lt;/a&gt;! Head to the official &lt;a href=&quot;https://www.ansible.com/overview/how-ansible-works&quot;&gt;site&lt;/a&gt; so as to get started with Ansible in case you haven’t done so already!&lt;/p&gt;

&lt;h4 id=&quot;say-to-hello-to-my-little-cloud-init-friend-with-the-voice-of-toni-montana-&quot;&gt;Say to hello to my little Cloud-Init friend (with the voice of Toni Montana) !&lt;/h4&gt;
&lt;p&gt;Red Hat’s official documentation has a great explanation regarding cloud-init and I think that I shouldn’t interfere thus I quote:&lt;br /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;“&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cloud-init&lt;/code&gt; is a software package that automates the initialization of cloud instances during system boot. You can configure &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cloud-init&lt;/code&gt; to perform a variety of tasks. Some sample tasks that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cloud-init&lt;/code&gt; can perform include:&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;Configuring a host name&lt;/li&gt;
    &lt;li&gt;Installing packages on an instance&lt;/li&gt;
    &lt;li&gt;Running scripts&lt;/li&gt;
    &lt;li&gt;Suppressing default virtual machine behaviour”&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;As you can understand there are many different scenarios that &lt;strong&gt;cloud-init&lt;/strong&gt; could come in quite handy.&lt;br /&gt;We will use this software to apply our own custom setup (&lt;em&gt;Static IP/Subnet/Resolv/DNS/Domain&lt;/em&gt; etc) for each and every vm that we create.&lt;/p&gt;

&lt;h3 id=&quot;prerequisites-&quot;&gt;Prerequisites 📝&lt;/h3&gt;

&lt;p&gt;In order to be able to understand and test the concept we will need the below setup/configurations available:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Our &lt;a href=&quot;https://www.ovirt.org/documentation/install-guide/chap-Installing_oVirt.html&quot;&gt;&lt;strong&gt;oVirt infrastructure&lt;/strong&gt;&lt;/a&gt; with a correctly configured &lt;a href=&quot;https://myhomelab.gr/linux/2020/01/20/replacing_ovirt_ssl.html&quot;&gt;SSL&lt;/a&gt; certificate (self signed or official one). You can always bypass the SSL configuration but you will need to adapt accordingly the commands that are used during the post as I’m using a secure connection, playbook wise.&lt;/li&gt;
  &lt;li&gt;a &lt;strong&gt;CentOS 7 virtual machine&lt;/strong&gt; ( hopefully the last machine that you will have to install and configure manually 🙏 ) that has ansible package installed and configured.&lt;br /&gt;This vm will host our yaml configuration files and eventually execute the playbooks that will create the new vm’s to our oVirt host. Check out this great &lt;a href=&quot;https://www.snel.com/support/how-to-install-ansible-on-centos-7/&quot;&gt;post&lt;/a&gt; if you haven’t configured one yet.&lt;/li&gt;
  &lt;li&gt;a &lt;strong&gt;Centos7 template with cloud-init&lt;/strong&gt; software configured. Again you can find lots of great posts regarding this concept of creating your own cloud-init ready CentOS ISO image or install and update your oVirt template. You can combine for example this cloud-init configuration &lt;a href=&quot;https://www.ibm.com/docs/en/powervc-cloud/2.0?topic=linux-installing-configuring-cloud-init-rhel&quot;&gt;post&lt;/a&gt; with my previous article on how to create your &lt;a href=&quot;https://myhomelab.gr/virtualization/2020/04/06/configure_vm_template.html&quot;&gt;own template&lt;/a&gt;!&lt;/li&gt;
  &lt;li&gt;In case you haven’t come across yet to one of my previous posts (for example on how to &lt;a href=&quot;https://myhomelab.gr/virtualization/2020/07/13/backup-virtual-machines.html&quot;&gt;backup your oVirt vm’s&lt;/a&gt;) , and even if it’s not a prerequisite for this post , it’s good to have &lt;strong&gt;Python SDK&lt;/strong&gt; installed for the &lt;strong&gt;oVirt Engine API&lt;/strong&gt; which is the collection of classes that will allows us to interact and run our oVirt system tasks.
&lt;strong&gt;For EL distros (such as CentOS, RHEL, etc.):&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;ovirt-tested-&lt;span class=&quot;nv&quot;&gt;$VERSION&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;baseurl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;http://resources.ovirt.org/repos/ovirt/tested/&lt;span class=&quot;nv&quot;&gt;$VERSION&lt;/span&gt;/rpm/el&lt;span class=&quot;nv&quot;&gt;$releasever&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;oVirt-&lt;span class=&quot;nv&quot;&gt;$VERSION&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1
&lt;span class=&quot;nv&quot;&gt;gpgcheck&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;yum &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;python-ovirt-engine-sdk4 ovirt-engine-sdk-python&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;or download the rpm directly from &lt;a href=&quot;http://ftp.iij.ad.jp/pub/linux/centos-vault/7.8.2003/virt/Source/ovirt-4.2/&quot;&gt;here&lt;/a&gt; in case you stumble upon package manager issues that are difficult for you to solve and install it manually,&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;rpm &lt;span class=&quot;nt&quot;&gt;-iv&lt;/span&gt; python-ovirt-engine-sdk4-4.2.7-1.el7.src.rpm&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;architecture-️&quot;&gt;Architecture ✏️&lt;/h3&gt;
&lt;p&gt;I find it quite useful to have a short sketch which can easily show me what I have already configured and what I want to achieve.&lt;/p&gt;

&lt;p&gt;Time for a drawing and more information 😅!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ansible-api/architecture.png&quot; data-lightbox=&quot;architecture&quot;&gt;
  &lt;img src=&quot;/assets/ansible-api/architecture.png&quot; title=&quot;architecture&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the above picture we see what we have already configured:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;oVirt Host&lt;/strong&gt; : inside of which I create VM’s, based on a template because i don’t want to configure users, packages, ssh keys etc, by following more or less these &lt;a href=&quot;https://www.linuxtechi.com/create-virtual-machines-ovirt-4-environment/&quot;&gt;steps&lt;/a&gt;. So you understand, or you’ll see on your own when creating more virtual machines, that those are a lot of clicks, especially if you intend to keep your services separated and in different machines.
    &lt;ul&gt;
      &lt;li&gt;&lt;em&gt;hostname&lt;/em&gt;: &lt;strong&gt;hl-ovirt&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;&lt;em&gt;DNS&lt;/em&gt;: &lt;strong&gt;ovirt.homelab.home&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;&lt;em&gt;IP&lt;/em&gt;: &lt;strong&gt;10.0.0.3&lt;/strong&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Ansible VM&lt;/strong&gt; : This is a virtual machine, created in our oVirt infrastructure and which hosts all the ansible related configuration/files.
    &lt;ul&gt;
      &lt;li&gt;&lt;em&gt;hostname&lt;/em&gt;: &lt;strong&gt;hl-ansible&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;&lt;em&gt;DNS&lt;/em&gt;: &lt;strong&gt;ansible.homelab.home&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;&lt;em&gt;IP&lt;/em&gt;: &lt;strong&gt;10.0.0.10&lt;/strong&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and what we want to achieve:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;our goal here is to be able to run a playbook from the &lt;strong&gt;ansible.homelab.home&lt;/strong&gt; which will call the &lt;strong&gt;REST-API&lt;/strong&gt; provided by the &lt;strong&gt;oVirt host&lt;/strong&gt; that will instruct the host to create a new machine with the &lt;strong&gt;Name&lt;/strong&gt; &amp;amp; &lt;strong&gt;IP&lt;/strong&gt; of our choosing by using the already configured &lt;strong&gt;cloud-init template&lt;/strong&gt; hosted on our oVirt host.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Confusing 😕? Nahhhh you, you’ll be alright, everything is going to be fine!&lt;/p&gt;

&lt;h3 id=&quot;configuration-&quot;&gt;Configuration 🔨&lt;/h3&gt;

&lt;p&gt;First step in projects that APIs are involved, is to configure the authorization and make sure that our account has appropriate permissions. I will start by trying to get a list of the installed VM’s that are hosted in my oVirt host and see what happens.&lt;/p&gt;

&lt;p&gt;I will use the admin user , the same one that I’m using to connect to the oVirt GUI, just for testing. In a production environment you should never use the admin account for various security reasons but for my local home-lab there is no need to go down that road!&lt;/p&gt;

&lt;p&gt;But before starting interacting with oVirt API, let’s see if we can fetch system related information on our host and make sure that &lt;strong&gt;Ansible&lt;/strong&gt; is correctly configured and has all the appropriate permissions.&lt;/p&gt;

&lt;h4 id=&quot;ansible-ad-hoc-&quot;&gt;Ansible Ad-Hoc 🏃&lt;/h4&gt;

&lt;p&gt;Let’s see in action some examples of what we can do with &lt;strong&gt;Ansible&lt;/strong&gt; and then we move to the playbooks part.&lt;/p&gt;

&lt;p&gt;I guess that you have a working ansible vm at this point and maybe another host that ansible can connect and retrieve informations from. You can always apply the commands below on your local machine but for our case we will stick to the plan!&lt;/p&gt;

&lt;p&gt;Let’s try and ping our oVirt host and confirm that we have connectivity:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;ansible ovirt.homelab.home &lt;span class=&quot;nt&quot;&gt;-m&lt;/span&gt; ping&lt;span class=&quot;s1&quot;&gt;&apos;
ovirt.homelab.home | SUCCESS =&amp;gt; {
    &quot;changed&quot;: false, 
    &quot;ping&quot;: &quot;pong&quot;
}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ok, we can connect to our host! Let’s check it’s hostname:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;ansible ovirt.homelab.home &lt;span class=&quot;nt&quot;&gt;-m&lt;/span&gt; shell &lt;span class=&quot;nt&quot;&gt;-a&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;echo $HOSTNAME&apos;&lt;/span&gt;
ovirt.homelab.home | SUCCESS | &lt;span class=&quot;nv&quot;&gt;rc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0 &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;
hl-ovirt&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;ohh nice! Let’s get the uptime of our host:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;ansible ovirt.homelab.home &lt;span class=&quot;nt&quot;&gt;-m&lt;/span&gt; shell &lt;span class=&quot;nt&quot;&gt;-a&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;uptime 
&lt;/span&gt;ovirt.homelab.home | SUCCESS | &lt;span class=&quot;nv&quot;&gt;rc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0 &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;
 14:18:13 up 8 days, 21:31,  2 &lt;span class=&quot;nb&quot;&gt;users&lt;/span&gt;,  load average: 0.10, 0.09, 0.11&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And we can do this for multiple servers! We can check RAM/CPU resources, copy/fetch files, change file/folder permissions, install packages and check if a service is up and running with the use of some ansible magic! Awesome right?&lt;/p&gt;

&lt;p&gt;Time to get to the grown up stuff though!&lt;/p&gt;

&lt;h4 id=&quot;ansible-playbooks-&quot;&gt;Ansible Playbooks 📚&lt;/h4&gt;

&lt;p&gt;I won’t get into too much detail on the playbook format and the spaces and dashes that needs to have as I take for a fact that you have some experience on &lt;strong&gt;Ansible&lt;/strong&gt; and if not please head to the &lt;strong&gt;Appendix&lt;/strong&gt; to find info and useful links.&lt;/p&gt;

&lt;p&gt;But in order to understand the next code snippets I must point out that &lt;em&gt;playbooks&lt;/em&gt; are expressed in &lt;strong&gt;YAML&lt;/strong&gt; format and composed of one or more ‘&lt;em&gt;plays&lt;/em&gt;’ that run in order from top to bottom against all machines matched by the host pattern.&lt;/p&gt;

&lt;p&gt;Start by adding below yaml code to a .yml file. I’m doing it with the use of EOF (End of file) operator so as to minimize any structure related yaml errors, so just copy/paste below output to your terminal:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yml&quot; data-lang=&quot;yml&quot;&gt;&lt;span class=&quot;s&quot;&gt;:~$ cat &amp;lt;&amp;lt;EOF &amp;gt; ovirt_list_vms.yml&lt;/span&gt;
&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;hosts&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ovirt.homelab.home&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;local&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;tasks&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Obtain SSO token&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ovirt_auth&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https://ovirt.homelab.home/ovirt-engine/api&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;admin@internal&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;lt;add&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;your&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;here&amp;gt;&quot;&lt;/span&gt;

  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;List vms&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ovirt_vms_facts&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;fetch_nested&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;nested_attributes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;description&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;auth&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ovirt_auth&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;}}&quot;&lt;/span&gt;

  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;set vms&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;set_fact&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
       &lt;span class=&quot;na&quot;&gt;vm&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;item.name&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;}}:&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;item.snapshots&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;map(attribute=&apos;description&apos;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;join(&apos;,&apos;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;}}&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;with_items&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ovirt_vms&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;}}&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;loop_control&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;item.name&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;}}&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;all_vms&lt;/span&gt;

  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;make a list&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;set_fact&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;vms=&quot;{{ all_vms.results | map(attribute=&apos;ansible_facts.vm&apos;) | list }}&quot;&lt;/span&gt;

  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Print vms&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;vms&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;EOF&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ok, don’t jump off the window yet, please 😆! If this is the first time that you stumble upon yaml code then keep in mind that the normal feeling after seeing the above is.. 😱 !!&lt;/p&gt;

&lt;p&gt;Let’s have a look on each section of the playbook. Keep in mind that I’m not an expert and you &lt;strong&gt;must not take the next steps break down as a granted&lt;/strong&gt;, this is my personal notes and understanding of the playbook that we’ve just created:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The first section is the &lt;em&gt;host&lt;/em&gt; which tells our playbook which is the target host (or group of hosts in other cases) that the playbook is going to be executed on. In our case the oVirt host of our infrastructure (&lt;strong&gt;ovirt.homelab.home&lt;/strong&gt;) which provides the &lt;strong&gt;REST-API&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;Next we have the &lt;em&gt;tasks&lt;/em&gt; section and the first &lt;em&gt;play&lt;/em&gt; where we are using the &lt;strong&gt;ovirt_auth&lt;/strong&gt; module to authenticate to oVirt engine and create an SSO token which will be used in the next play.&lt;/li&gt;
  &lt;li&gt;In the second play we are using &lt;strong&gt;ovirt_vms_facts module&lt;/strong&gt; to retrieve the description of all vm’s currently deployed in our home-lab infrastructure by using the SSO token created in the previous task. We could always also use a pattern instead of the a nested_attribute, for example :
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ovirt_vms_facts:
 pattern: name=centos* and cluster=&amp;lt;our_cluster_name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;Then we use the &lt;strong&gt;set_fact module&lt;/strong&gt; which allows us to dynamically add or change facts during execution. In our case we want to output a list of vm names and their snapshot description, filter &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(|)&lt;/code&gt; by description separated by comma for multiple snapshots (if there are any..) while looping though the active virtual machines. Then we register the output to &lt;strong&gt;all_vms&lt;/strong&gt; &lt;em&gt;variable&lt;/em&gt; so as to be filtered in the next play. Again this is just an example and you can create your own list columns with the help of a little &lt;strong&gt;JSON PATH&lt;/strong&gt; magic!&lt;br /&gt;We then apply another filter to the previous &lt;strong&gt;all_vms&lt;/strong&gt; variable with the use of map that just allows us to change every item in a list, using the ‘&lt;em&gt;attribute&lt;/em&gt;’ keyword and do the transformation based on attributes of the list elements and assign the list to a new &lt;em&gt;variable&lt;/em&gt; called &lt;strong&gt;vms&lt;/strong&gt;.&lt;br /&gt;To better understand what would be the output at this exact point, you can remove the &lt;strong&gt;“make a list”&lt;/strong&gt; play and in &lt;strong&gt;“print vms”&lt;/strong&gt; one replace &lt;strong&gt;vms&lt;/strong&gt; variable with the &lt;strong&gt;all_vms&lt;/strong&gt;. In that way you can test different tasks and figure out on your own what would be the outcome of each one if run separetly.&lt;br /&gt;This would be the output in such case (do you see the all_vms.result that we are using in the &lt;em&gt;“make a list”&lt;/em&gt; task?) :&lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yml&quot; data-lang=&quot;yml&quot;&gt;&lt;span class=&quot;na&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ovirt.homelab.home&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;=&amp;gt; {&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;all_vms&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;changed&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; 
        &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;msg&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;All&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;completed&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; 
        &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;results&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;_ansible_ignore_errors&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; 
                &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;_ansible_item_label&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;hl-dns&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; 
                &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;_ansible_item_result&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; 
                &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;_ansible_no_log&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; 
                &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ansible_facts&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;vm&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;hl-dns:&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Active&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;VM&quot;&lt;/span&gt;
                &lt;span class=&quot;pi&quot;&gt;},&lt;/span&gt; 
                &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;changed&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; 
                &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;failed&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; 
                &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;item&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;affinity_labels&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[],&lt;/span&gt; 
                    &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;applications&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[],&lt;/span&gt; 
                    &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;bios&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;boot_menu&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt;
                            &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;enabled&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;false&lt;/span&gt;
                        &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;pi&quot;&gt;},&lt;/span&gt; 
&lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;more output&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;ul&gt;
  &lt;li&gt;Finally we print the finished filtered list that is assigned to the &lt;strong&gt;vms&lt;/strong&gt; &lt;em&gt;variable&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And again, maybe I’m mistaken, maybe I didn’t explain something correctly or adequately so please either use the ansible-doc to &lt;em&gt;search&lt;/em&gt; for more detailed module information,&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;:~$ ansible-doc set_fact
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;or head to the official &lt;a href=&quot;https://www.ansible.com/overview/how-ansible-works&quot;&gt;Ansible documentation&lt;/a&gt; to clear up anything that seems incorrect or not legit! Then please drop me a line so as to update my notes 😂 🙏!&lt;/p&gt;

&lt;p&gt;Time to run our playbook and see what happens!&lt;/p&gt;

&lt;h3 id=&quot;execution-️&quot;&gt;Execution 🖱️&lt;/h3&gt;
&lt;p&gt;Open a terminal, head to the folder that you have saved our .yml file and run the command below:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ ansible-playbook ovirt_list_vms.yml 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;PLAY [ovirt.homelab.home] **********************************************************************************************************************************

TASK [Gathering Facts] **********************************************************************************************************************************
ok: [ovirt.homelab.home]

TASK [Obtain SSO token] *********************************************************************************************************************************
ok: [ovirt.homelab.home]

TASK [List vms] *****************************************************************************************************************************************
ok: [ovirt.homelab.home]

TASK [set vms] ******************************************************************************************************************************************
ok: [ovirt.homelab.home] =&amp;gt; (item=hl-ansible)
ok: [ovirt.homelab.home] =&amp;gt; (item=hl-dns)

TASK [make a list] **************************************************************************************************************************************
ok: [ovirt.homelab.home]

TASK [Print vms] ****************************************************************************************************************************************
ok: [ovirt.homelab.home] =&amp;gt; {
    &quot;vms&quot;: [
        &quot;hl-ansible: Active VM, before_ansible_setup&quot;, 
        &quot;hl-dns: Active VM&quot;
    ]
}

PLAY RECAP **********************************************************************************************************************************************
ovirt.homelab.home           : ok=6    changed=0    unreachable=0    failed=0   
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Success!!&lt;/p&gt;

&lt;p&gt;So our playbook did exactly what we told it to do! It connected to our oVirt via the REST-API, obtained an &lt;strong&gt;SSO token&lt;/strong&gt; based on the username/password login credentials and then created a list of our currently deployed virtual machines and their snapshot description (I forgot that I had a snapshot for my hl-ansible vm, time to delete that and save some time and backup space..) !&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Play Recap&lt;/strong&gt; output part, informs us that no failures and no changes were applied, as we didn’t update anything, and all 6 plays run successfully!🎉🎉&lt;/p&gt;

&lt;h3 id=&quot;intermission-&quot;&gt;Intermission ☕&lt;/h3&gt;

&lt;p&gt;It seems to me like a good point to conclude this first part. I have given more than enough information and configuration links that you can refer to in case you want to dig deeper on your own regarding both &lt;strong&gt;Ansible&lt;/strong&gt; and &lt;strong&gt;APIs&lt;/strong&gt; while waiting for part two!&lt;/p&gt;

&lt;p&gt;Kudos for managing to get to this point, really, even if you have a headache and you are seriously thinking of jumping to another job field! Before doing so though, grab a well reserved beverage, go through the notes, configure and play with &lt;strong&gt;Ansible&lt;/strong&gt; a bit and I think that you will change your mind!&lt;/p&gt;

&lt;p&gt;In the second part of this post, we will try to create our next virtual machine automatically via a playbook and maybe wrap it around a bash script! Stay tuned!! 🎸&lt;/p&gt;

&lt;p&gt;Thanks for reading, until the next post and as Dr Wallace Breen says in Half-Life 2.. &lt;br /&gt;Be wise. Be safe. Be aware!&lt;/p&gt;

</description>
        <pubDate>Sun, 05 Sep 2021 21:10:59 +0000</pubDate>
        <link>https://zroupas.github.io/linux/virtualization/automation/2021/09/05/ansible_ovirt_api_part1.html</link>
        <guid isPermaLink="true">https://zroupas.github.io/linux/virtualization/automation/2021/09/05/ansible_ovirt_api_part1.html</guid>
        
        <category>linux,ovirt,api,ansible</category>
        
        
        <category>linux</category>
        
        <category>Virtualization</category>
        
        <category>automation</category>
        
      </item>
    
      <item>
        <title>Pair Logitech MX Keys with Xubuntu 18.04 via Bluetooth</title>
        <description>&lt;h3 id=&quot;pair-logitech-mx-keys-with-xubuntu-1804-via-bluetooth&quot;&gt;Pair Logitech MX Keys with Xubuntu 18.04 via Bluetooth&lt;/h3&gt;

&lt;hr /&gt;

&lt;p&gt;Usually I don’t write posts about hardware configurations but this time I got really frustrated while trying to pair my new, much anticipated, fancy &lt;a href=&quot;https://www.logitech.com/en-us/products/keyboards/mx-keys-wireless-keyboard.920-009294.html&quot;&gt;Logitech MX Keys&lt;/a&gt; keyboard!
Maybe this is helpful to someone experiencing the same issue there is a &lt;em&gt;catch&lt;/em&gt; in the pairing procedure and Logitech’s documentation included in the package isn’t clear enough regarding this.&lt;/p&gt;

&lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;
&lt;p&gt;Let me start by saying that I’ve been using this keyboard for about a week and I’m totally pleased with my choice. It’s the first time that I bought a high-end keyboard, I was always using descent keyboards but never really gave more than 50 euros for any of them and happy with what I was getting with my money.
And honestly, why someone wouldn’t be happy with his &lt;a href=&quot;https://www.amazon.com/Microsoft-Comfort-Curve-Keyboard-2000/dp/B0009ZBRS0&quot;&gt;Microsoft Comfort Curve Keyboard 2000&lt;/a&gt; ?? Thank you for your services MS Comfort, you’ve been a loyal comrade!&lt;/p&gt;

&lt;p&gt;Then, Covid-19 hit the world and home office increased exponentially! So my home became my actual office, which meant having a keyboard/mouse for my gaming PC and a keyboard/mouse for my work laptop. Of course this is the least of the problems that this new situation brought along but still it didn’t make sense to have 2 full size keyboards/2mouses on a desk , wasting so much working space in 2021!&lt;/p&gt;

&lt;p&gt;I thought about getting a keyboard/mouse hub but from my working experience, I have never came across to a robust solution that would do the actual work. Every single time those hubs where extremely glitchy and I would end up swearing and throwing them away!&lt;/p&gt;

&lt;p&gt;So I though of buying a keyboard that would have both &lt;strong&gt;Bluetooth&lt;/strong&gt; &amp;amp; &lt;strong&gt;USB Adapter&lt;/strong&gt; available. In this way I could connect the keyboard to laptop via BT and use the USB adapter to my PC which doesn’t have BT!&lt;/p&gt;

&lt;p&gt;And my search led me to this rechargeable (USB Type C), stylish, nearly silent and great built design with &lt;strong&gt;smart backlighting&lt;/strong&gt; and ,based on the reviews, &lt;strong&gt;fantastic battery life&lt;/strong&gt; ( 10 days on full charge and 5 months with backlighting turned off which is awesome as I’m working in a well lighted environment )!&lt;/p&gt;

&lt;p&gt;Did I think of it correctly? Where there other solutions that would be better? Sure! Only time will show but honestly I wanted to treat myself with a new keyboard some time now 😆 !!&lt;/p&gt;

&lt;p&gt;Enough with the chit chat, let’s cut to the chase!&lt;/p&gt;

&lt;h3 id=&quot;pairing&quot;&gt;Pairing!&lt;/h3&gt;

&lt;p&gt;Now let’s head to the pairing &lt;em&gt;catch&lt;/em&gt; that you’ve probably came across and was the reason that landed you here!&lt;/p&gt;

&lt;p&gt;Here it was! My new robust keyboard in my hands and ready to start typing! Following the described procedure, I turned on the switch of the keyboard, opened my BT manager on the laptop and MX Keys new device directly popped up in the discovered devices.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/logi-mx-keys/bt_devices.png&quot; data-lightbox=&quot;bt_devices&quot;&gt;
  &lt;img src=&quot;/assets/logi-mx-keys/bt_devices.png&quot; title=&quot;bt_devices&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But when I chose &lt;strong&gt;Pair&lt;/strong&gt;,  multiple pop up pairing messages like “Pairing MX Keys” started flooding the right upper corner of my screen but I couldn’t figure out what was needed from my side.&lt;/p&gt;

&lt;p&gt;After googling a bit, I found the below image in Logitech’s official &lt;a href=&quot;https://www.logitech.com/en-us/promo/mxsetup/keyboard-setup/bluetooth.html&quot;&gt;Setup Guide&lt;/a&gt;..&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/logi-mx-keys/logi-setup.png&quot; data-lightbox=&quot;logi-setup&quot;&gt;
  &lt;img src=&quot;/assets/logi-mx-keys/logi-setup.png&quot; title=&quot;logi-setup&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;.. and it hit me! MX Keys wanted me to type a 6 digits code to my keyboard and hit ENTER! But for some reason my Xubuntu wasn’t giving me any code to use ..&lt;/p&gt;

&lt;p&gt;So back to google ✊ ! Eventually my search 🔎 led me to this very helpful &lt;a href=&quot;https://wiki.archlinux.org/title/Bluetooth_keyboard&quot;&gt;guide&lt;/a&gt; which explains how to the follow the exact same pairing steps that GUI is using but from the terminal ( always go with the terminal choice people, always! ).&lt;/p&gt;

&lt;h4 id=&quot;console-time&quot;&gt;Console Time!&lt;/h4&gt;
&lt;p&gt;We will use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bluetoothctl&lt;/code&gt; command to try and get the 6 digits code in order to pair our controller and the new keyboard. This command tool is provided by &lt;a href=&quot;https://launchpad.net/ubuntu/bionic/+package/bluez&quot;&gt;bluez_5.48-0ubuntu3_amd64&lt;/a&gt; 🐞 package.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Run the command to get at the prompt and tell bluetoothctl to look only for keyboards,
    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;:&amp;gt; bluetoothctl
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;bluetooth]# agent KeyboardOnly
Agent is already registered
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;Put your controller in &lt;strong&gt;&lt;em&gt;pairable&lt;/em&gt;&lt;/strong&gt; mode
    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;bluetooth]# pairable on
Changing pairable on succeeded
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;Instruct the controller to start &lt;strong&gt;&lt;em&gt;scanning&lt;/em&gt;&lt;/strong&gt; for a suitable device
    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;bluetooth]# scan on
Discovery started
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;CHG] Controller 38:DE:AD:2A:BE:CA Discovering: &lt;span class=&quot;nb&quot;&gt;yes&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;NEW] Device 52:9B:32:5E:BF:3E 47-6B-25-5E-BF-3E
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;NEW] Device D4:AD:1D:C5:77:B5 MX Keys
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;When the correct device is found, use the MAC to &lt;strong&gt;&lt;em&gt;pair&lt;/em&gt;&lt;/strong&gt;
    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;bluetooth]# pair D4:AD:1D:C5:77:B5
Attempting to pair with D4:AD:1D:C5:77:B5
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;CHG] Device D4:AD:1D:C5:77:B5 Connected: &lt;span class=&quot;nb&quot;&gt;yes&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;agent] Passkey: 508203
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;NEW] Primary Service
/org/bluez/hci0/dev_D4_AD_1D_C1_67_B4/service000a
00001801-0000-1000-8000-00805f9b34fb
Generic Attribute Profile
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;..]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;And there it is, the passkey is revelled! At this point type the 6 digit code to your MX keyboard, hit Enter and boom,&lt;/p&gt;
    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;..]
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;CHG] Device D4:AD:1D:C1:67:B4 ServicesResolved: &lt;span class=&quot;nb&quot;&gt;yes&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;CHG] Device D4:AD:1D:C1:67:B4 Paired: &lt;span class=&quot;nb&quot;&gt;yes
&lt;/span&gt;Pairing successful
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;The pairing via BT, which was previously failing, is now successful 🎉 !&lt;/p&gt;
    &lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From my point of view,  I think that Logitech’s quick guide included in the package should have a note regarding the &lt;strong&gt;6 digits code&lt;/strong&gt; needed to be typed in order to complete the pairing and not rely on people having to go through their online guide to find this important configuration step.
Of course Xubuntu’s bug of not showing the code on the other side is also something that should be addressed and fixed but maybe this is not a general issue and just something broken on my side!&lt;/p&gt;

&lt;p&gt;Either way, now that I’m typing this post in my new awesome keyboard I can say that it totally worth the search and fix 🙌! I can now get rid of all the unneeded keyboards and free up my desk space!&lt;/p&gt;

&lt;p&gt;Yes I know that i didn’t speak about an equivalent 1 mouse to save the world kind of thing but I have the feeling that this will be a much easier task. If not.. then expect a new post where I’m complaining about something else I guess 😂!&lt;/p&gt;

&lt;p&gt;Even though the are multiple great videos and reviews out there regarding this keyboard, I think that I should create my own after the first 3 months and compare notes of current and future behaviour! We will see 😊 ..&lt;/p&gt;

&lt;p&gt;Thanks for reading, until the next post and as Dr Wallace Breen says in Half-Life 2.. &lt;br /&gt;Be wise. Be safe. Be aware!&lt;/p&gt;

</description>
        <pubDate>Wed, 25 Aug 2021 19:32:59 +0000</pubDate>
        <link>https://zroupas.github.io/linux/2021/08/25/pair_mxkeys_xubuntu_bt.html</link>
        <guid isPermaLink="true">https://zroupas.github.io/linux/2021/08/25/pair_mxkeys_xubuntu_bt.html</guid>
        
        <category>linux,logitech,mxkeys,xubuntu,logi,bluetooth</category>
        
        
        <category>linux</category>
        
      </item>
    
      <item>
        <title>Create and Export Full oVirt VM Backups to NFS Storage Domain</title>
        <description>&lt;h4 id=&quot;create-and-export-full-ovirt-vm-backups-to-nfs-storage-domain&quot;&gt;Create and Export Full oVirt VM Backups to NFS Storage Domain.&lt;/h4&gt;

&lt;hr /&gt;

&lt;p&gt;Now that I had my &lt;a href=&quot;https://myhomelab.gr/virtualization/2020/04/06/configure_vm_template.html&quot;&gt;first virtual machine&lt;/a&gt; up and running and before moving on by configuring all the cool stuff that I had in mind, I needed to find a way to create daily virtual machine backups and store them in another device.&lt;/p&gt;

&lt;p&gt;You see, the most common nightmare among system engineers is the absence of a backup policy 😱 !There are a lot of different approaches when it comes to backup and it depends on the provided services and its criticality!&lt;/p&gt;

&lt;p&gt;You can have a Database backup, a File or Disk backup,a Bare metal or Virtual machine backup etc, then you have to decide if you want a Full or an Incremental one, Daily/Hourly/Weekly, locally or remotely and as you understand the task of creating a concrete backup policy can become a huge headache but in the end of the day is the only thing that will allow you to have some sleep at night!&lt;/p&gt;

&lt;h3 id=&quot;anchor&quot;&gt;Prerequisites 📝&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;As documented in the official oVirtBackup &lt;a href=&quot;https://github.com/wefixit-AT/oVirtBackup&quot;&gt;repo&lt;/a&gt;, in order to be able to use this tool it is necessary to install oVirt Python-sdk. At the time that i configured this tool the python-ovirt-engine-sdk4 package wasn’t available to CentOS repository so have a look via yum or download it from &lt;a href=&quot;https://cbs.centos.org/koji/buildinfo?buildID=23045&quot;&gt;here&lt;/a&gt; and install it via rpm as I did.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;CentOS/Fedora&lt;/strong&gt;&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;rpm &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; python-ovirt-engine-sdk4-4.2.7-1.el7.src.rpm&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;ul&gt;
  &lt;li&gt;(&lt;em&gt;Optional&lt;/em&gt;) A package that is also used in this guide and which will be needed in order to clone the public repository of oVritBackup is git and if you haven’t installed it yet, then use the below command to do so.&lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;yum &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;git&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;creating-and-attaching-nfs-storage-domain&quot;&gt;Creating and Attaching NFS Storage Domain&lt;/h3&gt;

&lt;h4 id=&quot;configure-nfs-in-qnap--optional-&quot;&gt;Configure NFS in QNAP ( Optional )&lt;/h4&gt;

&lt;p&gt;I will be using my QNAP NAS as an NFS server to store my backups remotely. If you have any machine lying around then you can setup your own NFS server without the need of a Nas. You can of course skip this step if you want to store the backups locally but keep in mind that the script that I will be using for backing up my virtaul machines needs an NFS export.&lt;/p&gt;

&lt;p&gt;All major descent NAS devices provide NFS functionality, I’m using a &lt;strong&gt;QNAP TS-409 Pro&lt;/strong&gt; which is quite old but does the job without any problems.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-export/qnap_login.png&quot; data-lightbox=&quot;qnap_login&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-export/qnap_login.png&quot; title=&quot;qnap_login&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First step is to enable NFS service by going to &lt;strong&gt;Network Services -&amp;gt; NFS Service&lt;/strong&gt;, click on &lt;strong&gt;Enable&lt;/strong&gt; and then Apply. Then we have to create a New Folder and set the NFS rights for the network share.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-export/qnap_nfs.png&quot; data-lightbox=&quot;qnap_nfs&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-export/qnap_nfs.png&quot; title=&quot;qnap_nfs&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Go to &lt;strong&gt;Access Right Management -&amp;gt; Share Folders&lt;/strong&gt; and click on New Share Folder. Follow the steps as you would in any other similar folder creation and after finishing choose &lt;strong&gt;NFS&lt;/strong&gt; from the &lt;strong&gt;Action Column&lt;/strong&gt; of the newly created folder ( we will give our folder an imaginative and original name, let’s call it nfs_share 😆 )&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-export/qnap_nfs_rights.png&quot; data-lightbox=&quot;qnap_nfs_rights&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-export/qnap_nfs_rights.png&quot; title=&quot;qnap_nfs_rights&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see from the above screenshot I have given full rights from my &lt;strong&gt;/24&lt;/strong&gt; internal network but you can limit it for specific IP’s. Click Apply and our NFS share is ready to be attached to our oVirt Storage Domains.&lt;/p&gt;

&lt;h4 id=&quot;mounting-nfs-share-on-centos7-optional&quot;&gt;Mounting NFS Share on CentOS7 (Optional)&lt;/h4&gt;

&lt;p&gt;You can bypass this section as this is not mandatory for the backup procedure. I would personally continue with the next steps as it will save you a lot of time and troubleshooting in case your &lt;strong&gt;NFS Export&lt;/strong&gt; fails to be attached to our oVirt infrastructure.&lt;/p&gt;

&lt;p&gt;First install the appropriate utils for your Centos7 to be able to act as an nfs client:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;yum &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;nfs-utils&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Create the path that your NFS will be mounted on&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; /mnt/nfs_share&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;and edit your /etc/fstab like below by changing your IP and share folder name:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;## You can also use your favorite editor&lt;/span&gt;
:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; &amp;gt;&amp;gt;/etc/fstab
10.0.0.4:/nfs_share /mnt/nfs_share/      nfs     defaults        0 0
EOF&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Let’s mount our new share and create a file so as to make sure that we have &lt;strong&gt;Write Permissions&lt;/strong&gt; which is of course required by oVirt export domain addition.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;mount &lt;span class=&quot;nt&quot;&gt;-a&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;touch&lt;/span&gt; /mnt/nfs_share/test
:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ls&lt;/span&gt; /mnt/nfs_share/test
&lt;span class=&quot;nt&quot;&gt;-rw-r--r--&lt;/span&gt;  1 root root    0 May  02 17:43 &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt;
:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; /mnt/nfs_share/test&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;preparing-and-adding-nfs-storage-domain-to-ovirt&quot;&gt;Preparing and Adding NFS Storage Domain to oVirt&lt;/h3&gt;

&lt;p&gt;Fire up your favorite browser, type your oVirt endpoint or IP and login with your admin account. By now I guess you have seen my previous posts on local &lt;a href=&quot;https://myhomelab.gr/linux/2019/12/13/local-ca-setup.html&quot;&gt;Certificate Authority&lt;/a&gt; and &lt;a href=&quot;https://myhomelab.gr/linux/2019/09/22/local-dns.html&quot;&gt;DNS&lt;/a&gt; configuration and you are familiar with your secure oVirt Administration Portal!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-vm/login.png&quot; data-lightbox=&quot;login&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-vm/login.png&quot; title=&quot;login&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From the left panel go to &lt;strong&gt;Storage&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Domains&lt;/strong&gt;. Now if you remember, and if not just have a look &lt;a href=&quot;https://myhomelab.gr/virtualization/2020/03/04/configure_cluster_host_ovirt.html&quot;&gt;here&lt;/a&gt; ⬅️, in the storage domain configuration I used in the first part of my oVirt setup I added two domains, a Data and an ISO.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-conf/domain_active.png&quot; data-lightbox=&quot;domain_active.png&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-conf/domain_active.png&quot; title=&quot;domain_active.png&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Time to add a new &lt;strong&gt;Export Domain&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;Click on &lt;strong&gt;New Domain&lt;/strong&gt; and add a Name, a Description and Choose NFS for Storage Type. In the Export Path use either FQDN or IP followed by the nfs folder that we created in the previous step, in my case &lt;strong&gt;10.0.0.4:/nfs_share&lt;/strong&gt; and click OK.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-export/export_domain.png&quot; data-lightbox=&quot;export_domain&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-export/export_domain.png&quot; title=&quot;export_domain&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If everything is ok with the NFS configuration, you will be able to see your NFS in an Active status as well as the Total and Free Disk Space! If not though, check your NFS server configuration ( share permissions, IP or FQDN etc)  or follow the previous section which documents the steps to mount NFS to our Centos7 and make sure that everything is configured exactly as they are documented.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-export/domain_active_export.png&quot; data-lightbox=&quot;domain_active_export&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-export/domain_active_export.png&quot; title=&quot;domain_active_export&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our NFS is now attached and ready to host all of our VM backups!&lt;/p&gt;

&lt;h3 id=&quot;creating-and-exporting-full-vm-backups-with-ovirtbackup-tool&quot;&gt;Creating and Exporting Full VM Backups with oVirtBackup Tool&lt;/h3&gt;

&lt;p&gt;oVirtBackup is a free tool written in Python which makes an awesome job on creating VM backups. I have been using this for the past months and never had a problem, this is a solid solution that you configure once and then forget about it. &lt;br /&gt;More information can be found in it’s official &lt;a href=&quot;https://github.com/wefixit-AT/oVirtBackup&quot;&gt;repository&lt;/a&gt; and I will document my configuration in order to get this up and running.&lt;/p&gt;

&lt;p&gt;Taken from the official Git page, the workflow that this script is using is:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Create a snapshot&lt;/li&gt;
  &lt;li&gt;Clone the snapshot into a new VM&lt;/li&gt;
  &lt;li&gt;Delete the snapshot&lt;/li&gt;
  &lt;li&gt;Delete previous backups (if set)&lt;/li&gt;
  &lt;li&gt;Export the VM to the NFS share&lt;/li&gt;
  &lt;li&gt;Delete the VM&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;ovirtbackup-setup&quot;&gt;oVirtBackup Setup&lt;/h4&gt;

&lt;p&gt;First step is to clone the repository! Navigate to your favorite folder or path that you keep your awesome scripts, run the following commands and&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; /home/user/scripts
:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git clone https://github.com/wefixit-AT/oVirtBackup.git&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h4 id=&quot;ovirtbackup-config&quot;&gt;oVirtBackup Config&lt;/h4&gt;

&lt;p&gt;The only file that you need to update in order to start creating backups is the &lt;strong&gt;config.cfg&lt;/strong&gt; (if this doesn’t exist just copy one from config_example.cfg).&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; /home/scripts/oVirtBackup
:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cp &lt;/span&gt;config_example.cfg config.cfg&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Let’s have a look at the basic lines found in the config file and that you need to edit in order to have this up and running.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;A list of names which VM’s should be backed up&lt;br /&gt;&lt;/em&gt;
&lt;strong&gt;vm_names&lt;/strong&gt;: [“myfirstvm”,”mysecondvm”] ## Check backup.py help for an option to backup all vm’s&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Url to connect to your engine&lt;br /&gt;&lt;/em&gt;
&lt;strong&gt;server&lt;/strong&gt;=https://&lt;FQDN&gt;/ovirt-engine/api&lt;/FQDN&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Username to connect to the engine&lt;br /&gt;&lt;/em&gt;
&lt;strong&gt;username&lt;/strong&gt;=admin@internal&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Password for the above user&lt;br /&gt;&lt;/em&gt;
&lt;strong&gt;password&lt;/strong&gt;=&lt;password&gt;&lt;/password&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Name of the NFS Export Domain (the one that we created in our first section)&lt;br /&gt;&lt;/em&gt;
&lt;strong&gt;export_domain&lt;/strong&gt;=nfs_share&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;The name of the cluster where the VM should be cloned&lt;br /&gt;&lt;/em&gt;
&lt;strong&gt;cluster_name&lt;/strong&gt;=ov_cluster&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Storage domain where the VM’s are located. This is important to check space usage during backup&lt;br /&gt;&lt;/em&gt;
&lt;strong&gt;storage_domain&lt;/strong&gt;=data&lt;br /&gt;&amp;lt;/em&amp;gt;
&lt;u&gt;&lt;i&gt;Optional&lt;/i&gt;&lt;u&gt; :&lt;/u&gt;&lt;/u&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Redirect log to a specific file&lt;br /&gt;&lt;/em&gt;
&lt;strong&gt;logger_file_path&lt;/strong&gt;=/var/log/vmbackup.log&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;How long backups should be keeped, this is in days&lt;br /&gt;&lt;/em&gt;
&lt;strong&gt;backup_keep_count&lt;/strong&gt;=1&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;How many backups should be kept, this is the number of backups to keep&lt;br /&gt;&lt;/em&gt;
&lt;strong&gt;backup_keep_count_by_number&lt;/strong&gt;=1&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So open the config.cfg with your favor editor ,add or update the appropriate lines and save your changes.&lt;/p&gt;

&lt;h4 id=&quot;our-first-vm-backup&quot;&gt;Our First VM Backup!&lt;/h4&gt;

&lt;p&gt;Now we are ready to start our first ever VM backup, let’s see the availble options that can be used,&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;./backup &lt;span class=&quot;nt&quot;&gt;-h&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;and let’s run it in a debug mode to see what happens!&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;/backup.py &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; config.cfg &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;If everything is ok and no error occurs then you should see logs like the ones below&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;2020-06-22 04:00:01,745: Start backup &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;: myfirstvm
2020-06-22 04:00:01,846: Snapshot creation started ...
2020-06-22 04:00:05,176: Snapshot operation&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;creation&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;progress ...
2020-06-22 04:00:10,268: Snapshot operation&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;creation&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;progress ...
2020-06-22 04:00:15,357: Snapshot operation&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;creation&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;progress ...
2020-06-22 04:00:20,437: Snapshot created
2020-06-22 04:00:30,533: Clone into VM &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;myfirstvm__20200622_040001&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; started ...
..
..
2020-06-22 04:01:33,503: Cloning &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;progress &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;VM myfirstvm__20200622_040001 status is &lt;span class=&quot;s1&quot;&gt;&apos;image_locked&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; ...
2020-06-22 04:01:38,536: Cloning &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;progress &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;VM myfirstvm__20200622_040001 status is &lt;span class=&quot;s1&quot;&gt;&apos;image_locked&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; ...
..
..
2020-06-22 04:02:59,918: Snapshot operation&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;deletion&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;progress ...
2020-06-22 04:03:05,007: Snapshot operation&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;deletion&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;progress ...
..
..
2020-06-22 04:07:21,170: Exporting &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;progress &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;VM myfirstvm__20200622_040001 status is &lt;span class=&quot;s1&quot;&gt;&apos;image_locked&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; ...
2020-06-22 04:07:26,214: Exporting &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;progress &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;VM myfirstvm__20200622_040001 status is &lt;span class=&quot;s1&quot;&gt;&apos;image_locked&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; ...
2020-06-22 04:07:31,251: Exporting finished
2020-06-22 04:07:31,278: Delete cloned VM &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;myfirstvm__20200622_040001&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; started ...
2020-06-22 04:07:33,485: Cloned VM &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;myfirstvm__20200622_040001&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; deleted
2020-06-22 04:07:33,485: Duration: 7:32 minutes
..&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The exported backups can be then found by going to &lt;strong&gt;Storage -&amp;gt; Domains -&amp;gt; nfs_share -&amp;gt; VM Import&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-export/nfs_share.png&quot; data-lightbox=&quot;nfs_share&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-export/nfs_share.png&quot; title=&quot;nfs_share&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can import any of the backups by right clicking and choosing &lt;strong&gt;Import&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-export/import_vm.png&quot; data-lightbox=&quot;import_vm&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-export/import_vm.png&quot; title=&quot;import_vm&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So now that the command is tested manually, it’s time to add it in a cronjob and create as many backups as you like! I’m running a backup at 4:00 AM every Monday,Wednesday,Saturday and a backup of all my VM’s every 1st Sunday of each Month!&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;crontab &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt;
0 4 &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; 1,3,5 /home/scripts/oVirtBackup/backup.py &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; /home/scripts/oVirtBackup/running.cfg &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt;
0 4 1-7 &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;date&lt;/span&gt; +&lt;span class=&quot;se&quot;&gt;\%&lt;/span&gt;a&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Sun&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; /home/scripts/oVirtBackup/backup.py &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; /home/scripts/oVirtBackup/running.cfg &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-a&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;strong&gt;Hint&lt;/strong&gt;: You can remove -d flag so as to avoid filling up your logs with unnecessary debug informaion. You can also add -a flag which will back all of your VM’s! Check –help as pointed out in previous section for more flag magic. 😉&lt;/p&gt;

&lt;h3 id=&quot;final-words-&quot;&gt;Final Words 💡&lt;/h3&gt;

&lt;p&gt;I don’t know how you feel but I am way more relaxed now than I was before configuring this awesome free tool! A huge thanks to &lt;a href=&quot;https://github.com/wefixit-AT&quot;&gt;wefixit-AT&lt;/a&gt; which is the repo owner!&lt;/p&gt;

&lt;p&gt;Our VM backups are successfully exported/imported to our NFS and can be used to either to revert to a previous state or even create a new VM to just get a configuration and then delete it!&lt;/p&gt;

&lt;p&gt;Having backups to revert to in case of a miscofiguration or HW failure is great. Now we can safely move on with our next projects in line!&lt;/p&gt;

&lt;p&gt;In the near future I will also document a simple script that I’m using to backup only the VM’s that are currently up and running, so stay tuned! 🎶🎶&lt;/p&gt;

&lt;p&gt;Thanks for reading, until the next post and as Dr Wallace Breen says in Half-Life 2.. &lt;br /&gt;Be wise. Be safe. Be aware!&lt;/p&gt;

</description>
        <pubDate>Mon, 13 Jul 2020 20:32:59 +0000</pubDate>
        <link>https://zroupas.github.io/virtualization/2020/07/13/backup-virtual-machines.html</link>
        <guid isPermaLink="true">https://zroupas.github.io/virtualization/2020/07/13/backup-virtual-machines.html</guid>
        
        <category>linux,ovirt,nfs,export,qnap,vm,backup</category>
        
        
        <category>Virtualization</category>
        
      </item>
    
      <item>
        <title>Configure oVirt Engine on CentOS 7 - Part Two</title>
        <description>&lt;h3 id=&quot;create-your-first-virtual-machine-and-save-it-as-a-template&quot;&gt;Create your first Virtual Machine and Save it as a Template.&lt;/h3&gt;

&lt;hr /&gt;

&lt;p&gt;In the &lt;a href=&quot;https://myhomelab.gr/virtualization/2020/03/04/configure_cluster_host_ovirt.html&quot;&gt;First Part&lt;/a&gt; of this tutorial, we documented in detail everything needed to setup our &lt;strong&gt;oVirt Infrastructure&lt;/strong&gt; which is ready to host new virtual machines.&lt;/p&gt;

&lt;p&gt;I suppose that you had enought time to familiarize yourself with the oVirt GUI by now, so let’s move on by creating our first virtual machine and save it as a template!&lt;/p&gt;

&lt;p&gt;After this step we will be finally ready to start our journey of conquering the world with your amazing projects!&lt;/p&gt;

&lt;h3 id=&quot;anchor&quot;&gt;Prerequisites 📝&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;In order to be able to connect to remote virtual machines and troubleshoot in case any of those becomes unavailable for any reason, we need to configure a &lt;strong&gt;Console Client&lt;/strong&gt;.&lt;br /&gt;I’m using the &lt;strong&gt;oVirt Remote Viewer&lt;/strong&gt; which can be either downloaded from the &lt;a href=&quot;https://virt-manager.org/download/&quot;&gt;Official Site&lt;/a&gt; for both &lt;strong&gt;Linux/Windows&lt;/strong&gt; or installed via a package manager:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;CentOS/Fedora&lt;/strong&gt;&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;yum &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; virt-viewer&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;strong&gt;Ubuntu/Mint&lt;/strong&gt;&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;virt-viewer&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;create-your-first-vm-️&quot;&gt;Create your First VM 🖥️&lt;/h3&gt;

&lt;p&gt;Fire up your favorite browser, type your oVirt endpoint or IP and login with your admin account. By now I guess you have seen my previous posts on local &lt;a href=&quot;https://myhomelab.gr/linux/2019/12/13/local-ca-setup.html&quot;&gt;Certificate Authority&lt;/a&gt; and &lt;a href=&quot;https://myhomelab.gr/linux/2019/09/22/local-dns.html&quot;&gt;DNS&lt;/a&gt; configuration and you are familiar with your secure oVirt Administration Panel!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-vm/login.png&quot; data-lightbox=&quot;login&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-vm/login.png&quot; title=&quot;login&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From the left panel go to &lt;strong&gt;Compute&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Virtual Machines&lt;/strong&gt;. As you can see our VM section is empty, so let’s not waste our precious time and create one!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-vm/initial_no_vm.png&quot; data-lightbox=&quot;initial_no_vm&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-vm/initial_no_vm.png&quot; title=&quot;initial_no_vm&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After clicking on &lt;strong&gt;New&lt;/strong&gt; button, we are introduced with the &lt;strong&gt;General Options&lt;/strong&gt; configuration Tab.&lt;br /&gt;Add a &lt;strong&gt;Name&lt;/strong&gt; for your first VM, a &lt;strong&gt;Description&lt;/strong&gt; and at the &lt;strong&gt;Instance Images&lt;/strong&gt; section click on &lt;strong&gt;Create&lt;/strong&gt; option to specify the disk space that is going to be used by your VM.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-vm/general.png&quot; data-lightbox=&quot;general&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-vm/general.png&quot; title=&quot;general&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Choose the disk size in &lt;strong&gt;GB&lt;/strong&gt; and leave the other options with the default values. In the bottom left, choose &lt;strong&gt;Show Advanced Options&lt;/strong&gt; and go to &lt;strong&gt;System&lt;/strong&gt; Tab.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-vm/disk.png&quot; data-lightbox=&quot;disk&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-vm/disk.png&quot; title=&quot;disk&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here we can choose the &lt;strong&gt;Memory Size&lt;/strong&gt; and &lt;strong&gt;Total CPU&lt;/strong&gt; that we want to assign to our virtual machine. We can also choose if we to install from a &lt;strong&gt;Template&lt;/strong&gt;, which is not possible for now as we haven’ configured one yet.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-vm/system.png&quot; data-lightbox=&quot;system&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-vm/system.png&quot; title=&quot;system&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Let me point out that there are a lot of different configuration options that can be changed based on your experience and needs, but I will only document the ones that are important for a basic VM creation.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So the last but not least configuration option is handled in the &lt;strong&gt;Boot Options&lt;/strong&gt; Tab.&lt;/p&gt;

&lt;p&gt;Enable &lt;strong&gt;Attach CD&lt;/strong&gt; and choose the ISO file that we have already uploaded and is documented in the &lt;strong&gt;Prerequisites&lt;/strong&gt; Section of &lt;a href=&quot;https://myhomelab.gr/virtualization/2020/03/04/configure_cluster_host_ovirt.html&quot;&gt;Part One&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-vm/boot.png&quot; data-lightbox=&quot;boot&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-vm/boot.png&quot; title=&quot;boot&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;OK&lt;/strong&gt; and &lt;em&gt;voilà&lt;/em&gt;! 🥂&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-vm/vm.png&quot; data-lightbox=&quot;vm&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-vm/vm.png&quot; title=&quot;vm&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hm.. I made it seem like a big thing by using a French word, right?&lt;/p&gt;

&lt;p&gt;Because it actually was!&lt;/p&gt;

&lt;p&gt;Like the time you created your first PC you had to go hunting for a CPU, RAM and a Hard Disk, among other important parts 😛 , now you just had those and needed to assign them to your new VM! &lt;strong&gt;Time to install our good old Penguin&lt;/strong&gt;!&lt;/p&gt;

&lt;h3 id=&quot;installing-centos7-to-our-vm-&quot;&gt;Installing CentOS7 to our VM 🔨&lt;/h3&gt;

&lt;p&gt;Let’s Run our VM and see what happens!&lt;/p&gt;

&lt;p&gt;Choose you virtual machine, click on &lt;strong&gt;Run&lt;/strong&gt; and then when Console is available, click on it and choose to open the file with the &lt;strong&gt;Remote Viewer&lt;/strong&gt;, which we have configured in the &lt;strong&gt;&lt;a href=&quot;#anchor&quot;&gt;Prerequisites&lt;/a&gt;&lt;/strong&gt; Section, in order to have a look on what happened when we booted up our VM.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-vm/boot_iso.png&quot; data-lightbox=&quot;boot_iso&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-vm/boot_iso.png&quot; title=&quot;boot_iso&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you followed the steps documented in the previous section then you should be able to start the Centos Installation Process by choosing the &lt;strong&gt;Install Centos 7&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Press &lt;strong&gt;Enter&lt;/strong&gt; to start the installation.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-vm/startup.png&quot; data-lightbox=&quot;startup&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-vm/startup.png&quot; title=&quot;startup&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Installation Intermission&lt;/strong&gt;: &lt;em&gt;Due to the fact that  there are a lot of great tutorials out there to help you get a new Centos7 up and running, I will skip the actual OS installation&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Instead here’s a gif that keeps you company during Centos7 installation! ⏱️&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-vm/installation.gif&quot; data-lightbox=&quot;installation&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-vm/installation.gif&quot; title=&quot;installation&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After the installation is finished you will be able to either connect to your VM via SSH or directly from the Console!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-vm/vnc.png&quot; data-lightbox=&quot;vnc&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-vm/vnc.png&quot; title=&quot;vnc&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congratulations, your first VM is now a reality! 🎉&lt;/p&gt;

&lt;h3 id=&quot;cleanup-and-export-your-vm-as-a-template-&quot;&gt;Cleanup and Export your VM as a Template 🚿&lt;/h3&gt;

&lt;p&gt;Templates can save us a huge amount of time in cases where we need to deploy a large number of similar VM’s, especially in a corporate environemt, but they are nothing more than a &lt;strong&gt;pre-installed/configured&lt;/strong&gt; virtual machine with it’s own OS and resources.&lt;/p&gt;

&lt;p&gt;I found templates to be very useful in my homelab setup where I’m using the same distribution and basic resources for all my VM’s. Whenever there is a need for a new virtual machine, I clone one from my Centos7 template, change it accordingly and have it ready in some minutes instead of going through all the steps documented in the previous section.&lt;/p&gt;

&lt;p&gt;In order to be able to use the template we have to configure and make it a bit generic. &lt;br /&gt;This means that we will have for example to:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Add a general hostname&lt;/li&gt;
  &lt;li&gt;Clear machine’s id&lt;/li&gt;
  &lt;li&gt;Remove HDADD value from network device&lt;/li&gt;
  &lt;li&gt;Delete any ssh keys and logs&lt;/li&gt;
  &lt;li&gt;Unconfigure VM&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this way every time we clone a machine, it will have it’s unique information which we can login and change/update appropriately.&lt;/p&gt;

&lt;h4 id=&quot;seal-your-vm-️&quot;&gt;Seal your VM 🛡️&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Login to your virtual machine&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Set a generic hostname&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;hostnamectl set-hostname localhost.localdomain&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;ul&gt;
  &lt;li&gt;Clear machine’s id&lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;:&amp;gt; /etc/machine-id&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;ul&gt;
  &lt;li&gt;Delete any host and root ssh keys&lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; /etc/ssh/ssh_host_&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;
:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-rf&lt;/span&gt; /root/.ssh/
:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; /root/anaconda-ks.cfg
:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; /root/.bash_history
:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;unset &lt;/span&gt;HISTFILE&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;ul&gt;
  &lt;li&gt;Configure Network Interface&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This step depends on your configuration as there are different changes based on the IP aasingment. My only note in this section is to &lt;strong&gt;remove&lt;/strong&gt; the &lt;em&gt;HWADDR=&lt;/em&gt; line. If you need more information you can check this great &lt;a href=&quot;https://github.com/rharmonson/richtech/wiki/CentOS-7-1511-Minimal-oVirt-Template&quot;&gt;Github&lt;/a&gt; page or on the official &lt;a href=&quot;https://www.ovirt.org/documentation/&quot;&gt;oVirt Documentation&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Install oVirt agent (Optional)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the oVirt management agent running inside the guest (our VM) which supplys run-time data and heart-beat info.&lt;br /&gt; Even though it’s great to have this enabled in every machine, i saw that it needs resources that i didn’t want to spare but if you want to install it here is the way to do so,&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;yum &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;epel-release
:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;yum &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;ovirt-guest-agent-common
:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;systemctl &lt;span class=&quot;nb&quot;&gt;enable &lt;/span&gt;ovirt-guest-agent &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; systemctl start ovirt-guest-agent&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;ul&gt;
  &lt;li&gt;Set VM to an unconfigured state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This final command will create a file called /.unconfigured and &lt;strong&gt;shutwdown the machine immediately&lt;/strong&gt;. The presence of this file will cause /etc/rc.d/rc.sysinit to act like it is the first time the machine is booting up and thus reconfigure networking/routing, time/date, services and delete all rules from /etc/udev/rules.d/,among other actions! You can check &lt;a href=&quot;https://linux.die.net/man/8/sys-unconfig&quot;&gt;sys-config man page&lt;/a&gt; for more info.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;sys-unconfig&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Our VM is now stopped and ready to be exported!&lt;/p&gt;

&lt;h4 id=&quot;export-vm-to-template-&quot;&gt;Export VM to Template 📤&lt;/h4&gt;

&lt;p&gt;Now in order to export our virtual machine to template, we have to choose it and click on the 3 dots found the upper right corner. Then choose &lt;strong&gt;Make Template&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-vm/make_template.png&quot; data-lightbox=&quot;make_template&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-vm/make_template.png&quot; title=&quot;make_template&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will open up the &lt;strong&gt;New Template&lt;/strong&gt; configuration where we can add a &lt;strong&gt;Name&lt;/strong&gt;, &lt;strong&gt;Description&lt;/strong&gt; or even change &lt;strong&gt;Disk Alias&lt;/strong&gt;(optional).&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-vm/new_template.png&quot; data-lightbox=&quot;new_template&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-vm/new_template.png&quot; title=&quot;new_template&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;OK&lt;/strong&gt; to start templating!&lt;/p&gt;

&lt;p&gt;You can check the progress in the &lt;strong&gt;Events&lt;/strong&gt; Tab.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-vm/task_started.png&quot; data-lightbox=&quot;task_started&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-vm/task_started.png&quot; title=&quot;task_started&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After this task is finished, we should be able to find our new template in &lt;strong&gt;Compute&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Templates&lt;/strong&gt; menu.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-vm/templates.png&quot; data-lightbox=&quot;templates&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-vm/templates.png&quot; title=&quot;templates&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our template is successfully created and waiting to be used! If you feel safe, just delete the already created VM and recreate, but this time use the template!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-vm/delete_first_vm.png&quot; data-lightbox=&quot;delete_first_vm&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-vm/delete_first_vm.png&quot; title=&quot;delete_first_vm&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-vm/initial_no_vm.png&quot; data-lightbox=&quot;initial_no_vm&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-vm/initial_no_vm.png&quot; title=&quot;initial_no_vm&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-vm/new_vm.png&quot; data-lightbox=&quot;new_vm&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-vm/new_vm.png&quot; title=&quot;new_vm&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on &lt;strong&gt;New VM&lt;/strong&gt; , add a &lt;strong&gt;Name&lt;/strong&gt; and click OK!&lt;/p&gt;

&lt;h3 id=&quot;final-words-&quot;&gt;Final Words 💡&lt;/h3&gt;

&lt;p&gt;You are finally able to start creating as many VM’s or templates as you like based on your oVirt hardware capacity!&lt;/p&gt;

&lt;p&gt;There are so many advantages on having your own homelab to play with and experiment without having to spend a fortune!&lt;br /&gt;
I hope those two detailed parts provided a better understanding on which are the most important ones that will help you get started on &lt;em&gt;configuring your own Open Source Virtual Infrastructure&lt;/em&gt; and &lt;em&gt;creating your first virtual machine&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In the near future I will also document the cloud-init configuration that I’m using in order to create new virtual machines automatically via &lt;strong&gt;Ansible&lt;/strong&gt; through the oVirt API, so stay tuned! 🎶🎶&lt;/p&gt;

&lt;p&gt;Thanks for reading, until the next post and as Dr Wallace Breen says in Half-Life 2.. &lt;br /&gt;Be wise. Be safe. Be aware!&lt;/p&gt;

</description>
        <pubDate>Mon, 06 Apr 2020 20:32:59 +0000</pubDate>
        <link>https://zroupas.github.io/virtualization/2020/04/06/configure_vm_template.html</link>
        <guid isPermaLink="true">https://zroupas.github.io/virtualization/2020/04/06/configure_vm_template.html</guid>
        
        <category>linux,ovirt,centos7,templates</category>
        
        
        <category>Virtualization</category>
        
      </item>
    
      <item>
        <title>Configure oVirt Engine on CentOS 7 - Part One</title>
        <description>&lt;h3 id=&quot;create-a-basic-clusterhost-and-configure-a-storage-data-and-iso-domain&quot;&gt;Create a basic Cluster/Host and Configure a Storage Data and ISO Domain.&lt;/h3&gt;

&lt;hr /&gt;

&lt;p&gt;In my previous &lt;a href=&quot;https://myhomelab.gr/virtualization/2019/09/01/ovirt.html&quot;&gt;post&lt;/a&gt; I gave a general overview of my oVirt setup that followed the &lt;a href=&quot;https://www.ovirt.org/documentation/install-guide/chap-Installing_oVirt.html&quot;&gt;official&lt;/a&gt; installation guidelines and default suggested values.&lt;/p&gt;

&lt;p&gt;So it’s time to provide a more detailed configuration on how to prepare everything, from a new cluster and host to ISO domain and local storage, in order to start creating new virtual machines.&lt;/p&gt;

&lt;p&gt;In the second part of this tutorial I will also create a VM and save it as a template which will be used for all of our future machines! (&lt;strong&gt;Note to Future Me&lt;/strong&gt;: Automate VM creation via the oVirt API!)&lt;/p&gt;

&lt;p&gt;Let’s move on!&lt;/p&gt;

&lt;h3 id=&quot;anchor&quot;&gt;Prerequisites&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;In case you want to configure ovirt-engine and host on the same machine as I did, you can login to the server that ovirt-engine is installed and in the hosts file add something like this (&lt;strong&gt;replace with your own IP and FQDN&lt;/strong&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;10.0.0.3 	node1.homelab.home&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This FQDN will be used at a later point when asked to provide an IP address for the configured Host.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Create the appropriate folders that will host the virtual hard disks, ovf files and ISO files. I am using &lt;strong&gt;/opt&lt;/strong&gt; but you can use any other local or network path based on the configuration you have in mind,&lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; /opt/&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;ovirt_data,ovirt_iso&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;chown &lt;/span&gt;vdsm:kvm /opt/&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;ovirt_data,ovirt_iso&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;ul&gt;
  &lt;li&gt;We need an ISO file in order to setup our first VM after we finish the Cluster/Host configuration. For this step you can download your favorite distribution and either setup a Samba Share for &lt;strong&gt;/opt/ovirt_iso&lt;/strong&gt; and upload iso files from your Windows machine or just scp them from your Unix workstation,&lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;wget &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
http://ftp.ntua.gr/pub/linux/centos/7.7.1908/isos/x86_64/CentOS-7-x86_64-Minimal-1908.iso
:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;scp CentOS-7-x86_64-Minimal-1708.iso &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
root@10.0.0.3:/opt/ovirt_iso/&amp;lt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;/images/11111111-1111-1111-1111-111111111111/&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;ul&gt;
  &lt;li&gt;As a final step I have also added an entry like the one below to my local hosts file,&lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;##&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Host Database&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# localhost is used to configure the loopback interface&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# when the system is booting.  Do not change this entry.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;##&lt;/span&gt;
127.0.0.1	localhost
10.0.0.3	ovirt.homelab.home&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;so as to be able to login to my oVirt endpoint without having to remember the IP of the oVirt host.&lt;br /&gt;(&lt;strong&gt;NOTE&lt;/strong&gt;: If you want to get rid of those uggly host entries, I will start doing so after this tutorial, checkout my post on how to create your own local DNS found &lt;a href=&quot;https://myhomelab.gr/linux/2019/09/22/local-dns.html&quot;&gt;here&lt;/a&gt;!)&lt;/p&gt;

&lt;h3 id=&quot;ovirt-data-centerclusterhost-configuration&quot;&gt;oVirt Data Center/Cluster/Host Configuration&lt;/h3&gt;

&lt;p&gt;If you are the type of person that doesn’t start copying commands from random tutorials and wants to have the background behind any of the configuration documented below, then have a look on the official &lt;a href=&quot;https://www.ovirt.org/documentation/admin-guide/chap-Data_Centers.html&quot;&gt;Data Centers&lt;/a&gt; and &lt;a href=&quot;https://www.ovirt.org/documentation/admin-guide/chap-Clusters.html&quot;&gt;Clusters&lt;/a&gt; documentation before proceeding.&lt;/p&gt;

&lt;p&gt;If you are like me though, open your favourite browser and type your oVirt endpoint, in my case &lt;strong&gt;https://ovirt.homelab.home/ovirt-engine/&lt;/strong&gt;. Login with the admin account which was created during the initial engine setup.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-conf/login.png&quot; data-lightbox=&quot;login&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-conf/login.png&quot; title=&quot;login&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see from the screenshot above the connection is secure because as I have already documented in my previous posts, I have configured my own Self-Signed wildcard certificate and replaced the default one provided by oVirt installation with my own! If you want to do the same, just have a look &lt;a href=&quot;https://myhomelab.gr/linux/2019/12/13/local-ca-setup.html&quot;&gt;here&lt;/a&gt; and &lt;a href=&quot;https://myhomelab.gr/linux/2020/01/20/replacing_ovirt_ssl.html&quot;&gt;here&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;After our successful login we are presented with the Dashboard. This is where we will find a general overview of the status of your Host, our Global CPU/Memory/Storage information and Cluster Utilization.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-conf/dashboard.png&quot; data-lightbox=&quot;dashboard&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-conf/dashboard.png&quot; title=&quot;dashboard&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You will have all the time in the world to wonder around so for now let’s cut to the chase!&lt;/p&gt;

&lt;p&gt;From the left panel go to &lt;strong&gt;Compute&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Data Centers&lt;/strong&gt;, select the Default and Remove it! Trust me!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-conf/delete.png&quot; data-lightbox=&quot;delete&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-conf/delete.png&quot; title=&quot;delete&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on New, choose a Name and a &lt;strong&gt;Local&lt;/strong&gt; Storage Type.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-conf/datacenter.png&quot; data-lightbox=&quot;datacenter&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-conf/datacenter.png&quot; title=&quot;datacenter&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After clicking Ok, it’s &lt;strong&gt;Cluster’s&lt;/strong&gt; turn!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-conf/cluster.png&quot; data-lightbox=&quot;cluster&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-conf/cluster.png&quot; title=&quot;cluster&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add a Name, a Description and in the &lt;strong&gt;Optimization Tab&lt;/strong&gt; choose &lt;strong&gt;Memory Optimization&lt;/strong&gt; -&amp;gt; &lt;strong&gt;For Desktop Load&lt;/strong&gt; which will allow you to overallocate memory and run more virtual machines.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-conf/cluster1.png&quot; data-lightbox=&quot;cluster1&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-conf/cluster1.png&quot; title=&quot;cluster1&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally &lt;strong&gt;Enable Memory Balloon&lt;/strong&gt; and click Ok.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-conf/cluster2.png&quot; data-lightbox=&quot;cluster2&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-conf/cluster2.png&quot; title=&quot;cluster2&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Final step will be to setup a &lt;strong&gt;Host&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-conf/host.png&quot; data-lightbox=&quot;host&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-conf/host.png&quot; title=&quot;host&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Type a Name, an IP Address (or an FQDN as pointed out in the &lt;strong&gt;&lt;a href=&quot;#anchor&quot;&gt;Prerequisites&lt;/a&gt;&lt;/strong&gt; Section), the root password for the host that is going to be configured (in my case the same machine that the engine is installed) and in the &lt;strong&gt;Advanced Parameters&lt;/strong&gt; deselect the &lt;strong&gt;Automatically configure host firewall&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-conf/host1.png&quot; data-lightbox=&quot;host1&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-conf/host1.png&quot; title=&quot;host1&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you click Ok, the engine will start installing apropriate packages to the configured host as shown in the &lt;strong&gt;Host Status&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-conf/host_status.png&quot; data-lightbox=&quot;host_status&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-conf/host_status.png&quot; title=&quot;host_status&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also see the installation progress in the &lt;strong&gt;Events Menu&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-conf/host_events.png&quot; data-lightbox=&quot;host_events&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-conf/host_events.png&quot; title=&quot;host_events&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the host is ready, we have a final chapter in order to complete the basic configuration needed for creating virtual machines and this is the &lt;strong&gt;Storage&lt;/strong&gt;!&lt;/p&gt;

&lt;h3 id=&quot;ovirt-dataiso-storage-configuration&quot;&gt;oVirt Data/ISO Storage Configuration&lt;/h3&gt;

&lt;p&gt;Directly from the official oVirt documentation:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;oVirt uses a centralized storage system for virtual machine disk images, ISO files, snapshots and provides three types of storage domains:&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;Data Domain: A data domain holds the virtual hard disks and OVF files of all the virtual machines and templates in a data center. In addition, snapshots of the virtual machines are also stored in the data domain&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;ISO Domain: ISO domains store ISO files (or logical CDs) used to install and boot operating systems and applications for the virtual machines. An ISO domain removes the data center’s need for physical media. An ISO domain can be shared across different data centers. ISO domains can only be NFS-based. Only one ISO domain can be added to a data center&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Export Domain: Export domains are temporary storage repositories that are used to copy and move images between data centers and oVirt environments. Export domains can be used to backup virtual machines. An export domain can be moved between data centers, however, it can only be active in one data center at a time. Export domains can only be NFS-based. Only one export domain can be added to a data center&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you need more information head to the &lt;a href=&quot;https://www.ovirt.org/documentation/admin-guide/chap-Storage.html&quot;&gt;official&lt;/a&gt; Storage Chapter. For our case we will skip the Export Domain for now and will only create the Data and ISO domains.&lt;/p&gt;

&lt;p&gt;Navigate to &lt;strong&gt;Storage&lt;/strong&gt; and click on &lt;strong&gt;New Domain&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-conf/data_domain.png&quot; data-lightbox=&quot;data_domain&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-conf/data_domain.png&quot; title=&quot;data_domain&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Choose &lt;strong&gt;Data&lt;/strong&gt; as a Domain Function, &lt;strong&gt;Local on Host&lt;/strong&gt; as Storage Type, add a Name and finally the Path which is already created in the &lt;strong&gt;&lt;a href=&quot;#anchor&quot;&gt;Prerequisites&lt;/a&gt;&lt;/strong&gt; Section, &lt;strong&gt;/opt/ovirt_data&lt;/strong&gt;.&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Do the same for iso domain but choose &lt;strong&gt;ISO&lt;/strong&gt; for a Domain Function and change the path to &lt;strong&gt;/opt/ovirt_iso&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Check that both data and iso domains are &lt;strong&gt;Active&lt;/strong&gt;, then click on the newly iso created domain and then choose &lt;strong&gt;Images Tab&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-conf/domain_active.png&quot; data-lightbox=&quot;domain_active&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-conf/domain_active.png&quot; title=&quot;domain_active&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If everything is correct then you should be able to see the iso that we have already uploaded in the &lt;strong&gt;&lt;a href=&quot;#anchor&quot;&gt;Prerequisites&lt;/a&gt;&lt;/strong&gt; Section!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-conf/centos_iso.png&quot; data-lightbox=&quot;centos_iso&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-conf/centos_iso.png&quot; title=&quot;centos_iso&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In case you don’t want to upload any iso file, you can also check the &lt;strong&gt;ovirt-image-repository&lt;/strong&gt; domain which as the name clearly states is a repository that contains images from all major distributions (CentOS,Fedora,Ubuntu,Debian etc) that are ready to be exported as virual machines.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/ovirt-conf/ovirt-repo.png&quot; data-lightbox=&quot;ovirt-repo&quot;&gt;
  &lt;img src=&quot;/assets/ovirt-conf/ovirt-repo.png&quot; title=&quot;ovirt-repo&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first part of the tutorial is finished and oVirt is now configured and ready to host new kick-ass virtual machines!!&lt;/p&gt;

&lt;h3 id=&quot;final-words&quot;&gt;Final Words&lt;/h3&gt;

&lt;p&gt;Rome wasn’t built in a day, right?&lt;/p&gt;

&lt;p&gt;Time to take a break and familiarize with your new infrastructure. Get confortable with all the available tabs and configuration options, before moving to Part Two which will show you how to create your first Virtual Machine and save it as a Template for future use!&lt;/p&gt;

&lt;p&gt;Thanks for reading, until the next post and as Dr Wallace Breen says in Half-Life 2.. Be wise. Be safe. Be aware!&lt;/p&gt;

</description>
        <pubDate>Wed, 04 Mar 2020 20:32:59 +0000</pubDate>
        <link>https://zroupas.github.io/virtualization/2020/03/04/configure_cluster_host_ovirt.html</link>
        <guid isPermaLink="true">https://zroupas.github.io/virtualization/2020/03/04/configure_cluster_host_ovirt.html</guid>
        
        <category>linux,</category>
        
        <category>ovirt</category>
        
        
        <category>Virtualization</category>
        
      </item>
    
  </channel>
</rss>
