{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": [
     "remove-cell",
     "remove_cell"
    ]
   },
   "outputs": [],
   "source": [
    "my_notebook_text = \"\"\"\n",
    "# Package management\n",
    "\n",
    "### Outline\n",
    "\n",
    "- Discuss software installation workflow.\n",
    "- Debian and Ubuntu packages.\n",
    "- Advanced Package Tool  (APT) for package installation, removal, search, and query.\n",
    "- Debian package tool (dpkg) for package installation, removal, and query.\n",
    "- Redhat packages.\n",
    "- Yellowdog Updater Modified and Dandified (YUM and DNF) for package installation, removal, search, and query.\n",
    "- Redhat Package Manager (RPM) for package installation, removal, and query\n",
    "\n",
    "***\n",
    "\n",
    "\n",
    "### The purpose of Linux packages\n",
    "\n",
    "All software in Linux distros come in the form of packages available in online mirrors.\n",
    "Packages are used for the following tasks:\n",
    "\n",
    "- Install software.\n",
    "- Remove software.\n",
    "- Update installed software.\n",
    "- Fix broken or partly removed software files.\n",
    "- Reconfigure installed software.\n",
    "- Verify the integrity of the installed files.\n",
    "***\n",
    "\n",
    "### Package format and tools in various Linux distributions\n",
    "\n",
    "- Debian and Ubuntu: *deb*, *apt*\n",
    "- Redhat: *rpm*, *dnf*\n",
    "- Arch and Monjaro: *packman*\n",
    "- Gentoo: *portage (emerge)*\n",
    "\n",
    "Modern tools, including *snap* and *flatpack*:\n",
    "\n",
    "- Universal software packaging (Ubuntu, Fedora, Monjaro).\n",
    "- Sandboxing.\n",
    "- All dependencies are included in the sandboxes.\n",
    "- Limited use: works for specific application installations.\n",
    "\n",
    "***\n",
    "\n",
    "\n",
    "### Linux package management\n",
    "\n",
    "\n",
    "```{figure} img/packagemanagers.png\n",
    "---\n",
    "height: 600px\n",
    "name: Debian_Redhat_pkg\n",
    "---\n",
    "Debian and Redhat package installation flow\n",
    "```\n",
    "\n",
    "\n",
    "\n",
    "***\n",
    "\n",
    "### Ubuntu (Debian) packages\n",
    "\n",
    "Ubuntu OS components and most of the GNU software are available in form of packages.\n",
    "A package file contains:\n",
    "\n",
    "- Software\n",
    "- Info (control) file\n",
    "- Scripts (pre/post install/remove)\n",
    "- md5, sha1, sha256, sha512 file hashes\n",
    "- Naming convention:\n",
    "\n",
    "```\n",
    "(package-name)_(source version)-(package-version)_(architecture).deb\n",
    "```\n",
    "\n",
    "For example:\n",
    "\n",
    "```\n",
    "make_4.4.1-3_amd64.deb\n",
    "```\n",
    "\n",
    "Package name is make. The source version is ```4.4.1``` of GNU Make 4.4.1 series, package version (revision): ```3``` from Debian, architecture: amd64.\n",
    "\n",
    "The official Ubuntu distribution located in the Ubuntu archive, folder <b>main</b> and <b>Restricted</b>. \n",
    "In folder Restricted, there are packages with restricted vendor licensing, for example, nvidia-driver-595.\n",
    "\n",
    "<b>Universe</b> Is free software available from the network, but not officialy maintained by Ubuntu development team. For example, a2ps (\"Anything to PS\"), xemacs21. No guarantee of security fixes and support.\n",
    "\n",
    "<b>Multiverse</b> Packages in the archive have some onerous license condition restricting use or redistribution of the software. For example, dvd-slideshow.\n",
    "\n",
    "Developers may offer their own package repositories, called package personal archives (<b>PPA</b>).\n",
    "\n",
    "You can run command ```apt-cache show``` on a package on Ubuntu system to see what folder the package belongs to:\n",
    "\n",
    "**Debian system APT folders**\n",
    "\n",
    "- main\n",
    "- contrib\n",
    "- non-free\n",
    "- non-free-firmware\n",
    "\n",
    "***\n",
    "\n",
    "### Deploy kvm3 VM (Exercise)\n",
    "\n",
    "Clone kvm1 into kvm3:\n",
    "```bash\n",
    "virsh shutdown kvm1\n",
    "virt-clone -o kvm1 -n kvm3 -f /home/hostadm/KVM/kvm3.qcow2\n",
    "```\n",
    "\n",
    "Start kvm3 VM and login to its console via virsh:\n",
    "```bash\n",
    "virsh start kvm3\n",
    "virsh console kvm3\n",
    "```\n",
    "\n",
    "Fix the hostname by editing file `/etc/hostname` with `nano` editor:\n",
    "```bash\n",
    "sudo -s\n",
    "nano /etc/hostname\n",
    "```\n",
    "change kvm1 for kvm3.\n",
    "\n",
    "Run script \n",
    "```bash\n",
    "vm_id_reset.sh\n",
    "```\n",
    "Reboot the VM:\n",
    "```bash\n",
    "reboot\n",
    "```\n",
    "\n",
    "***\n",
    "\n",
    "### Installing Ubuntu packages with APT (Exercises)\n",
    "\n",
    "Login to kvm3.\n",
    "\n",
    "Try executing command make, which doesn't exist on the VM yet:\n",
    "```bash\n",
    "make\n",
    "```\n",
    "The system error comes:  <I>The program 'make' is currently not installed. </I></br>\n",
    "\n",
    "You can install it by typing: ```sudo apt install``` make\n",
    "Install recommended package make by running ```apt install```:\n",
    "```bash\n",
    "apt install make\n",
    "```\n",
    "You should be able to run command make now.\n",
    "\n",
    "Simulate package installation by using option -s:\n",
    "```bash\n",
    "apt install -s netpbm\n",
    "```\n",
    "Notice the prerequisite library package that would get installed, libnetpbm10\n",
    "Download the package without installation:\n",
    "```bash\n",
    "apt install -d netpbm\n",
    "```\n",
    "Notice the deb files with the packages in the apt cache directory:\n",
    "```bash\n",
    "ls -l /var/cache/apt/archives\n",
    "```\n",
    "Another way to download a package and get it in the current working directory:\n",
    "```bash\n",
    "apt download netpbm\n",
    "```\n",
    "Install the package:\n",
    "```bash\n",
    "apt install netpbm\n",
    "```\n",
    "Updating all the installed packages\n",
    "```bash\n",
    "apt update\n",
    "apt upgrade\n",
    "```\n",
    "The APT repository and software folders are defined in file\n",
    "```/etc/apt/sources.list``` and optionally in directory ```/etc/apt/sources.list.d```\n",
    "\n",
    "<hr>\n",
    "\n",
    "### Removing Ubuntu packages with APT (Exercises)\n",
    "\n",
    "Remove package make by running ```apt remove```:\n",
    "\n",
    "```bash\n",
    "apt remove make\n",
    "```\n",
    "Simulate package removal by using option -s:\n",
    "```bash\n",
    "apt remove -s netpbm\n",
    "```\n",
    "Notice package libnetpbm10 won't be removed\n",
    "Simulate package removal with the dependencies:\n",
    "```bash\n",
    "apt autoremove -s netpbm\n",
    "```\n",
    "Notice the both packages would be removed.\n",
    "Remove the package with the dependencies:\n",
    "```bash\n",
    "apt autoremove netpbm\n",
    "```\n",
    "Both netpbm and libnetpbm10 should be gone now.\n",
    "\n",
    "<hr>\n",
    "\n",
    "### Search and quiry Ubuntu packages with APT (Exercises)\n",
    "\n",
    "First, update the available package list from the Ubuntu repository:\n",
    "```bash\n",
    "apt update\n",
    "```\n",
    "Search for packages containing string make in their name or description:\n",
    "```bash\n",
    "apt-cache search make\n",
    "```\n",
    "Narrow down the search results for the names containing make:\n",
    "```bash\n",
    "apt-cache search --names-only make\n",
    "```\n",
    "Filter the output for word make\n",
    "```bash\n",
    "apt-cache search --names-only make | grep -w ^make\n",
    "```\n",
    "Get the information about package make:\n",
    "```bash\n",
    "apt-cache show make\n",
    "```\n",
    "List the packages that depend on package make. Forcefully removing ```make``` would break these packages.\n",
    "```bash\n",
    "apt-cache showpkg make\n",
    "```\n",
    "Show the packages a given package depends on:\n",
    "```\n",
    "apt-cache depends make\n",
    "```\n",
    "\n",
    "\n",
    "<hr>\n",
    "\n",
    "\n",
    "## Query package repository folder (Exercises).\n",
    "\n",
    "```apt-cache show``` on a package reveals what folder the package belongs to.\n",
    "It can be **main**, **restricted**, **universe**, and **multiverse**.\n",
    "\n",
    "```bash\n",
    "apt-cache show make | grep Filename\n",
    "```\n",
    "```json\n",
    "Filename: pool/main/m/make-dfsg/make_4.4.1-3_amd64.deb\n",
    "```\n",
    "\n",
    "\n",
    "```bash\n",
    "apt-cache show nvidia-driver-595 | grep Filename\n",
    "```\n",
    "\n",
    "```json\n",
    "Filename: pool/restricted/n/nvidia-graphics-drivers-595/nvidia-driver-595_595.71.05-0ubuntu0.26.04.1_amd64.deb\n",
    "Filename: pool/restricted/n/nvidia-graphics-drivers-595/nvidia-driver-595_595.58.03-0ubuntu2_amd64.deb\n",
    "```\n",
    "\n",
    "```bash\n",
    "apt-cache show a2ps | grep Filename\n",
    "```\n",
    "\n",
    "```json\n",
    "Filename: pool/universe/a/a2ps/a2ps_4.15.7-5_amd64.deb\n",
    "```\n",
    "\n",
    "```bash\n",
    "apt-cache show dvd-slideshow | grep Filename\n",
    "```\n",
    "\n",
    "```json\n",
    "Filename: pool/multiverse/d/dvd-slideshow/dvd-slideshow_0.8.6.1-2build1_all.deb\n",
    "```\n",
    "\n",
    "\n",
    "### APT security (Exercise)\n",
    "\n",
    "- Packages spooled into /var/cache/apt/archives/ directory before installation.\n",
    "- File ```Packages``` (catalog) contains the MD5, SHA1, SHA256, SHA512 hashes, which are presented via ```apt-cache show``` command.\n",
    "- ```apt install``` verifies the hash of the installable package file with the published one.\n",
    "- The cached ```Packages``` file content is stored in /var/lib/apt/ directory.\n",
    "- The ```Packages``` file is signed with the maintainer's Gnu Privacy Guard (GPG) key.\n",
    "- The maintainers public GPG keys are stored in directory /usr/share/keyrings/\n",
    "\n",
    "On Ubuntu 26.04, the maintainers gpg public keys are stored in directory /usr/share/keyrings/.\n",
    "On older Ubuntu distributions, there were only two GPG keys for any distro stored in directory /etc/apt/trusted.gpg.d/.\n",
    "\n",
    "Let's see what happens to APT when the GPG keys are removed.\n",
    " Remove the gpg public keys:\n",
    "```bash\n",
    "rm  /usr/share/keyrings/*\n",
    "```\n",
    "Run command below and see the error messages about unverifiable signature:\n",
    "```bash\n",
    "apt update\n",
    "```\n",
    "Restore the public keys:\n",
    "```bash\n",
    "apt install  ubuntu-keyring --reinstall\n",
    "```\n",
    "\n",
    "Run ```apt update``` again.\n",
    "\n",
    "***\n",
    "\n",
    "### Adding APT repository (Exercise)\n",
    "\n",
    "New apt lists should be either added to file /etc/apt/sources.list or added as new files in directory\n",
    " /etc/apt/sources.list.d\n",
    " \n",
    "For example, lets add the repository for BeeGFS file system.\n",
    "\n",
    "Download file ```beegfs-noble.list``` from the vendor's web site into directory /etc/apt/sources.list.d:\n",
    "```bash\n",
    "cd /etc/apt/sources.list.d\n",
    "wget https://www.beegfs.io/release/beegfs_8.3/dists/beegfs-noble.list\n",
    "```\n",
    "\n",
    "Download the vendor public key, dearmor, and store it in directory /etc/apt/keyrings:\n",
    "```bash\n",
    "cd /tmp\n",
    "wget https://www.beegfs.io/release/beegfs_8.3/gpg/GPG-KEY-beegfs\n",
    "cat GPG-KEY-beegfs  | sudo gpg --dearmor -o /etc/apt/keyrings/GPG-KEY-beegfs.gpg\n",
    "```\n",
    "\n",
    "Reference the key in file ```/etc/apt/sources.list.d/beegfs-noble.list```:\n",
    "\n",
    "<font size=\"3\">\n",
    "    \n",
    "```{admonition} content of beegfs-noble.list file\n",
    "deb **[signed-by=/etc/apt/keyrings/GPG-KEY-beegfs.gpg]** https://www.beegfs.io/release/beegfs_8.3 noble non-free\n",
    "```\n",
    "</font>\n",
    "\n",
    "Update apt caches:\n",
    "```bash\n",
    "apt update\n",
    "```\n",
    "Install a package from the newly added repository:\n",
    "```bash\n",
    "apt install beegfs-tools\n",
    "```\n",
    "***\n",
    "\n",
    "### Install a specific version of a package with APT (Exercise)\n",
    "\n",
    "There maybe several versions of the same package available in a repository.\n",
    "\n",
    "You can see them all with command ```apt-cache show``` followed by the package name.\n",
    "\n",
    "For example, for the kernel package ```linux-image-virtual``` we can see several versions:\n",
    "```bash\n",
    "apt-cache show linux-image-virtual\n",
    "```\n",
    "Let's install version ```7.0.0-31.31``` with command below:\n",
    "```bash\n",
    "apt install linux-image-virtual=7.0.0-31.31\n",
    "```\n",
    "***\n",
    "\n",
    "\n",
    "### APT logs\n",
    "\n",
    "The history of all ```apt``` commands is stored in file /var/log/apt/history.log\n",
    "\n",
    "To browse through the ```apt``` history:\n",
    "```bash\n",
    "less /var/log/apt/history.log\n",
    "```\n",
    "\n",
    "To see the last 20 lines of the history:\n",
    "```bash\n",
    "tail -20 /var/log/apt/history.log\n",
    "```\n",
    "\n",
    "***\n",
    "\n",
    "### Quiry Ubuntu packages with dpkg (Exercises)\n",
    "\n",
    "What packages are installed on the system?\n",
    "```bash\n",
    "dpkg -l\n",
    "```\n",
    "Quiry package status with dpkg:\n",
    "```bash\n",
    "dpkg -s make\n",
    "dpkg -s tzdata\n",
    "```\n",
    "List the files contained in the package:\n",
    "```bash\n",
    "dpkg -L tzdata\n",
    "```\n",
    "What package contains a file? For example, command /bin/ls\n",
    "```bash\n",
    "dpkg -S /bin/ls\n",
    "```\n",
    "Reconfigure a package with command dpkg-reconfigure after installation:\n",
    "```bash\n",
    "dpkg-reconfigure tzdata\n",
    "```\n",
    "To see the current package configuration, command debconf-show can be used, for example:\n",
    "```bash\n",
    "debconf-show tzdata\n",
    "```\n",
    "If there is no package dependencies, then a package can be installed with command dpkg. Otherwise, use APT.\n",
    "Install package make:\n",
    "```bash\n",
    "apt download make\n",
    "dpkg -i make_4.4.1-3_amd64.deb\n",
    "```\n",
    "To see the list of the files, contained in the deb package file:\n",
    "```bash\n",
    "dpkg --contents make_4.4.1-3_amd64.deb\n",
    "```\n",
    "\n",
    "Verify the consistency of installed files from a package:\n",
    "```bash\n",
    "dpkg -V make\n",
    "```\n",
    "There is nothing in the output.\n",
    "\n",
    "Let's modify a content of one of the files that comes with the package, ```/usr/include/gnumake.h```:\n",
    "```bash\n",
    "echo '/* last line */' >> /usr/include/gnumake.h\n",
    "```\n",
    "Remove file ```/usr/share/man/man1/make.1.gz```:\n",
    "```bash\n",
    "rm /usr/share/man/man1/make.1.gz\n",
    "```\n",
    "Run the package consistency check and see the tampered and removed files in the list:\n",
    "```bash\n",
    "dpkg -V make\n",
    "```\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "Remove package make by using command dpkg:\n",
    "```bash\n",
    "dpkg --purge make\n",
    "```\n",
    "\n",
    "Extract the package content into new directory PKGdir:\n",
    "```bash\n",
    "dpkg -X make_4.4.1-3_amd64.deb PKGdir\n",
    "```\n",
    "\n",
    "Install package ```tree```:\n",
    "```bash\n",
    "apt install tree\n",
    "```\n",
    "\n",
    "Browse the directory tree with command `tree`:\n",
    "```bash\n",
    "tree PKGdir\n",
    "```\n",
    "<hr>\n",
    "\n",
    "\n",
    "### RedHat packages\n",
    "\n",
    "RPM Package naming convention:\n",
    "```\n",
    "(package-name)-(source version)-(package release).(architecture).rpm\n",
    "```\n",
    "For example,\n",
    "```bash\n",
    "nano-8.1-3.el10.x86_64.rpm\n",
    "```\n",
    "Package name is nano. The source version is 8.1. The package release is 3.el10 (Release 3 for RedHat Enterprise Linux 10). Architecture is x86_64.\n",
    "\n",
    "<hr>\n",
    "\n",
    "### Deploy rocky10-1 VM (Exercise)\n",
    "\n",
    "Clone your rocky10 VM into rocky10-1, \n",
    "```bash\n",
    "virt-clone -o rocky10 -n rocky10-1 -f /home/hostadm/KVM/rocky10-1.qcow2\n",
    "```\n",
    "\n",
    "\n",
    "Start rocky10-1 and login to its console via command virsh console: \n",
    "```\n",
    "virsh start rocky10-1\n",
    "virsh console rocky10-1\n",
    "```\n",
    "\n",
    "Fix the hostname in file /etc/hostname for the correct one, ```rocky10-1```\n",
    "\n",
    "Run the following commands to reset the machine-id or rocky10-1 and release its DHCP IP address:\n",
    "```bash\n",
    "sudo dnf install dbus-tools\n",
    "sudo rm -f /etc/machine-id\n",
    "sudo dbus-uuidgen --ensure=/etc/machine-id\n",
    "sudo dbus-uuidgen --ensure\n",
    "sudo ip addr flush dev enp1s0\n",
    "```\n",
    "Reboot the VM:\n",
    "```bash\n",
    "sudo reboot\n",
    "```\n",
    "***\n",
    "\n",
    "### Installing and removing RedHat packages with DNF (Exercises)\n",
    "\n",
    "DNF or Dandified YUM is the next-generation version of the Yellowdog Updater, Modified (yum), a package manager for .rpm-based Linux distributions such as RedHat, Fedora, and Rocky. \n",
    "\n",
    "Clean the dnf cache:\n",
    "```bash\n",
    "dnf clean all\n",
    "```\n",
    "\n",
    "Install package netpbm on it:\n",
    "```bash\n",
    "dnf install netpbm\n",
    "```\n",
    "Reinstall package netpbm:\n",
    "```bash\n",
    "dnf reinstall netpbm\n",
    "```\n",
    "You can download packages without installation, for example:\n",
    "```bash\n",
    "dnf download make\n",
    "ls -l make*\n",
    "```\n",
    "Check available updates for installed packages:\n",
    "```bash\n",
    "dnf check-update\n",
    "```\n",
    "Update package tzdata\n",
    "```bash\n",
    "dnf update tzdata\n",
    "```\n",
    "Updating all the installed packages:\n",
    "```bash\n",
    "dnf update\n",
    "```\n",
    "To remove an installed package:\n",
    "```bash\n",
    "dnf remove netpbm\n",
    "```\n",
    "The same can be accomplished with:\n",
    "```bash\n",
    "dnf erase netpbm\n",
    "```\n",
    "\n",
    "<hr>\n",
    "\n",
    "### Search and quiry RPM packages with DNF (Exercises)\n",
    "\n",
    "List the installed package and the package available for installation\n",
    "```bash\n",
    "dnf list gsl\n",
    "dnf list coreutils\n",
    "```\n",
    "The output shows three columns - the package name and architecture, the package version, and the repository. \n",
    "The latter package showes up as installed (@ in front of the repo), and the former as available for installation.\n",
    "\n",
    "To see just installed packages:\n",
    "```bash\n",
    "dnf list --installed\n",
    "dnf repoquery --installed\n",
    "```\n",
    "\n",
    "To see packages available for installation:\n",
    "```bash\n",
    "dnf list --available\n",
    "dnf repoquery\n",
    "```\n",
    "\n",
    "\n",
    "Search for packages containing string make in their name or description:\n",
    "```bash\n",
    "dnf search make\n",
    "```\n",
    "Get the information about package make:\n",
    "```bash\n",
    "dnf info make\n",
    "```\n",
    "List the packages that package make depends upon:\n",
    "```bash\n",
    "dnf deplist make\n",
    "```\n",
    "\n",
    "Identify the package that contains a specific file, for example ```/bin/ls```:\n",
    "```bash\n",
    "dnf provides /usr/bin/ls\n",
    "```\n",
    "\n",
    "\n",
    "***\n",
    "\n",
    "### DNF repositories (Exercises)\n",
    "\n",
    "Like in Ubuntu, the Redhat packages are sorted in repositories.\n",
    "\n",
    "\n",
    "The dnf repositores are defined in directory ```/etc/yum.repos.d/*.repo```\n",
    "\n",
    "\n",
    "To see the available package repositories:\n",
    "```bash\n",
    "dnf repolist\n",
    "```\n",
    "- ```BaseOS```\n",
    "- ```AppStream```\n",
    "- ```Extras```\n",
    "\n",
    "See disabled repositories:\n",
    "```bash\n",
    "dnf repolist --disabled\n",
    "```\n",
    "Packages can be installed only from enabled repositories.\n",
    "\n",
    "To enable, for example repository ```baseos-source```:\n",
    "```bash\n",
    "dnf config-manager --enable  baseos-source\n",
    "```\n",
    "Check the list of available repos:\n",
    "```bash\n",
    "dnf repolist\n",
    "```\n",
    "\n",
    "To see the packages in repo ```baseos-source```:\n",
    "```bash\n",
    "dnf list --repo baseos-source\n",
    "```\n",
    "\n",
    "Add an external vendor repo, for example, BeeGFS with the .repo file in [URL](https://www.beegfs.io/release/beegfs_8.3/dists/beegfs-rhel10.repo):\n",
    "```bash\n",
    "dnf config-manager --add-repo https://www.beegfs.io/release/beegfs_8.3/dists/beegfs-rhel10.repo\n",
    "```\n",
    "\n",
    "Import the vendor key:\n",
    "```bash\n",
    "rpm --import https://www.beegfs.io/release/beegfs_8.3/gpg/GPG-KEY-beegfs\n",
    "```\n",
    "\n",
    "Check the list of available repos:\n",
    "```bash\n",
    "dnf repolist\n",
    "```\n",
    "\n",
    "See repo file ```beegfs-rhel10.repo``` in directory /etc/yum.repos.d:\n",
    "```bash\n",
    "ls /etc/yum.repos.d\n",
    "```\n",
    "\n",
    "Install a package from the new repo:\n",
    "```bash\n",
    "dnf install beegfs-tools\n",
    "```\n",
    "\n",
    "To disable the repo:\n",
    "```bash\n",
    "dnf config-manager --disable beegfs\n",
    "```\n",
    "\n",
    "Check the list of available repos:\n",
    "```bash\n",
    "dnf repolist\n",
    "```\n",
    "\n",
    "To remove the repo completely from the system:\n",
    "```bash\n",
    "rm /etc/yum.repos.d/beegfs-rhel10.repo\n",
    "```\n",
    "\n",
    "\n",
    "***\n",
    "\n",
    "\n",
    "### Advanced DNF functionalities\n",
    "\n",
    "History of DNF transactions:\n",
    "```bash\n",
    "dnf history\n",
    "```\n",
    "\n",
    "It is possible to revert dnf installation or upgrade in case the old pkgs are still available in thge repos.\n",
    "For example to revert transaction 10:\n",
    "```bash\n",
    "dnf history undo 10\n",
    "```\n",
    "\n",
    "Roll back to transaction 9:\n",
    "```bash\n",
    "dnf history rollback 9\n",
    "```\n",
    "\n",
    "DNF also provides bundles of packages by ```groups```:\n",
    "\n",
    "```bash\n",
    "dnf group list\n",
    "```\n",
    "\n",
    "The detailed info on dnf can be found in the RedHat docs:\n",
    "\n",
    "[Managing software with the DNF tool](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/pdf/managing_software_with_the_dnf_tool/red_hat_enterprise_linux-9-managing_software_with_the_dnf_tool-en-us.pdf)\n",
    "\n",
    "***\n",
    "\n",
    "### Query RPM packages with rpm (Exercises)\n",
    "What packages are installed on the system?\n",
    "```bash\n",
    "rpm -qa\n",
    "```\n",
    "A friendly readable list comes after sorting the output:\n",
    "```bash\n",
    "rpm -qa | sort | less\n",
    "```\n",
    "\n",
    "If there is no package dependencies or the dependencies have already been resolved, then a package can be simply installed with command rpm from its file. Otherwise, use DNF.\n",
    "Install package make from its rpm file:\n",
    "```bash\n",
    "rpm -ivh make-4.4.1-9.el10.x86_64.rpm\n",
    "```\n",
    "\n",
    "Quiry package status with rpm:\n",
    "```bash\n",
    "rpm -q make\n",
    "```\n",
    "Display information about the installed package:\n",
    "```bash\n",
    "rpm -qi make\n",
    "```\n",
    "List the files contained in the package:\n",
    "```bash\n",
    "rpm -ql make\n",
    "```\n",
    "Which package owns a file? For example, command /bin/ls\n",
    "```bash\n",
    "rpm -qf /bin/ls\n",
    "```\n",
    "RPM dependencies:\n",
    "```bash\n",
    "rpm -qR make\n",
    "```\n",
    "\n",
    "Verify the consistency of installed files from a package:\n",
    "```bash\n",
    "rpm -V make\n",
    "```\n",
    "There is nothing in the output.\n",
    "\n",
    "Let's modify a content of one of the files that comes with the package, /usr/include/gnumake.h:\n",
    "```bash\n",
    "echo '/* last line */' >> /usr/include/gnumake.h\n",
    "```\n",
    "Remove file /usr/share/man/man1/make.1.gz:\n",
    "```bash\n",
    "rm /usr/share/man/man1/make.1.gz\n",
    "```\n",
    "Run the package consistency check and see the tampered and removed files in the list:\n",
    "```bash\n",
    "rpm -V make\n",
    "```\n",
    "\n",
    "Try to remove package make by using command rpm:\n",
    "```bash\n",
    "rpm -e make\n",
    "```\n",
    "\n",
    "\n",
    "Quiry the package file:\n",
    "```bash\n",
    "rpm -qp  make-4.4.1-9.el10.x86_64.rpm\n",
    "rpm -qlp make-4.4.1-9.el10.x86_64.rpm\n",
    "rpm -qip make-4.4.1-9.el10.x86_64.rpm\n",
    "```\n",
    "The second command above shows the files contained in the package file, and the third command displays the info about the package.\n",
    "More info about command rpm can be found in the man pages:\n",
    "```bash\n",
    "man rpm\n",
    "```\n",
    "\n",
    "<hr>\n",
    "\n",
    "### References on APT and YUM\n",
    "\n",
    "\n",
    "[Package Management table for rpm and deb packages](https://help.ubuntu.com/community/SwitchingToUbuntu/FromLinux/RedHatEnterpriseLinuxAndFedora)\n",
    "\n",
    "[Learn Linux, 101: Debian package management](https://developer.ibm.com/tutorials/l-lpic1-102-4/)\n",
    "\n",
    "[Learn Linux, 101: RPM and YUM package management](https://developer.ibm.com/tutorials/l-lpic1-102-5/)\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": [
     "remove-cell",
     "remove_cell"
    ]
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Both GOOGLE_API_KEY and GEMINI_API_KEY are set. Using GOOGLE_API_KEY.\n"
     ]
    },
    {
     "ename": "ServerError",
     "evalue": "503 UNAVAILABLE. {'error': {'code': 503, 'message': 'This model is currently experiencing high demand. Spikes in demand are usually temporary. Please try again later.', 'status': 'UNAVAILABLE'}}",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mServerError\u001b[39m                               Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[3]\u001b[39m\u001b[32m, line 57\u001b[39m\n\u001b[32m     53\u001b[39m     output_text = response.text\n\u001b[32m     54\u001b[39m     print(output_text)\n\u001b[32m     55\u001b[39m \n\u001b[32m     56\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m APIError \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[32m---> \u001b[39m\u001b[32m57\u001b[39m     \u001b[38;5;28;01mraise\u001b[39;00m e\n",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[3]\u001b[39m\u001b[32m, line 57\u001b[39m\n\u001b[32m     53\u001b[39m     output_text = response.text\n\u001b[32m     54\u001b[39m     print(output_text)\n\u001b[32m     55\u001b[39m \n\u001b[32m     56\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m APIError \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[32m---> \u001b[39m\u001b[32m57\u001b[39m     \u001b[38;5;28;01mraise\u001b[39;00m e\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/lib/python3.13/site-packages/google/genai/models.py:6549\u001b[39m, in \u001b[36mModels.generate_content\u001b[39m\u001b[34m(self, model, contents, config)\u001b[39m\n\u001b[32m   6545\u001b[39m   parsed_config_to_call = _extra_utils.get_usage_header(\n\u001b[32m   6546\u001b[39m       parsed_config_to_call\n\u001b[32m   6547\u001b[39m   )\n\u001b[32m   6548\u001b[39m i += \u001b[32m1\u001b[39m\n\u001b[32m-> \u001b[39m\u001b[32m6549\u001b[39m response = \u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43m_generate_content\u001b[39;49m\u001b[30;43m(\u001b[39;49m\n\u001b[32m   6550\u001b[39m \u001b[30;43m    \u001b[39;49m\u001b[30;43mmodel\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mmodel\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mcontents\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mcontents\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mconfig\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mparsed_config_to_call\u001b[39;49m\n\u001b[32m   6551\u001b[39m \u001b[30;43m\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n\u001b[32m   6553\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m function_map:\n\u001b[32m   6554\u001b[39m   \u001b[38;5;28;01mbreak\u001b[39;00m\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/lib/python3.13/site-packages/google/genai/models.py:4977\u001b[39m, in \u001b[36mModels._generate_content\u001b[39m\u001b[34m(self, model, contents, config)\u001b[39m\n\u001b[32m   4974\u001b[39m request_dict = _common.convert_to_dict(request_dict)\n\u001b[32m   4975\u001b[39m request_dict = _common.encode_unserializable_types(request_dict)\n\u001b[32m-> \u001b[39m\u001b[32m4977\u001b[39m response = \u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43m_api_client\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mrequest\u001b[39;49m\u001b[30;43m(\u001b[39;49m\n\u001b[32m   4978\u001b[39m \u001b[30;43m    \u001b[39;49m\u001b[30;43m'\u001b[39;49m\u001b[30;43mpost\u001b[39;49m\u001b[30;43m'\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mpath\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mrequest_dict\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mhttp_options\u001b[39;49m\n\u001b[32m   4979\u001b[39m \u001b[30;43m\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n\u001b[32m   4981\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m config \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mgetattr\u001b[39m(\n\u001b[32m   4982\u001b[39m     config, \u001b[33m'\u001b[39m\u001b[33mshould_return_http_response\u001b[39m\u001b[33m'\u001b[39m, \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m   4983\u001b[39m ):\n\u001b[32m   4984\u001b[39m   return_value = types.GenerateContentResponse(sdk_http_response=response)\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/lib/python3.13/site-packages/google/genai/_api_client.py:1650\u001b[39m, in \u001b[36mBaseApiClient.request\u001b[39m\u001b[34m(self, http_method, path, request_dict, http_options)\u001b[39m\n\u001b[32m   1640\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mrequest\u001b[39m(\n\u001b[32m   1641\u001b[39m     \u001b[38;5;28mself\u001b[39m,\n\u001b[32m   1642\u001b[39m     http_method: \u001b[38;5;28mstr\u001b[39m,\n\u001b[32m   (...)\u001b[39m\u001b[32m   1645\u001b[39m     http_options: Optional[HttpOptionsOrDict] = \u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[32m   1646\u001b[39m ) -> SdkHttpResponse:\n\u001b[32m   1647\u001b[39m   http_request = \u001b[38;5;28mself\u001b[39m._build_request(\n\u001b[32m   1648\u001b[39m       http_method, path, request_dict, http_options\n\u001b[32m   1649\u001b[39m   )\n\u001b[32m-> \u001b[39m\u001b[32m1650\u001b[39m   response = \u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43m_request\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43mhttp_request\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mhttp_options\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mstream\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43;01mFalse\u001b[39;49;00m\u001b[30;43m)\u001b[39;49m\n\u001b[32m   1651\u001b[39m   response_body = (\n\u001b[32m   1652\u001b[39m       response.response_stream[\u001b[32m0\u001b[39m] \u001b[38;5;28;01mif\u001b[39;00m response.response_stream \u001b[38;5;28;01melse\u001b[39;00m \u001b[33m'\u001b[39m\u001b[33m'\u001b[39m\n\u001b[32m   1653\u001b[39m   )\n\u001b[32m   1654\u001b[39m   \u001b[38;5;28;01mreturn\u001b[39;00m SdkHttpResponse(headers=response.headers, body=response_body)\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/lib/python3.13/site-packages/google/genai/_api_client.py:1443\u001b[39m, in \u001b[36mBaseApiClient._request\u001b[39m\u001b[34m(self, http_request, http_options, stream)\u001b[39m\n\u001b[32m   1440\u001b[39m     retry = tenacity.Retrying(**retry_kwargs)\n\u001b[32m   1441\u001b[39m     \u001b[38;5;28;01mreturn\u001b[39;00m retry(\u001b[38;5;28mself\u001b[39m._request_once, http_request, stream)  \u001b[38;5;66;03m# type: ignore[no-any-return]\u001b[39;00m\n\u001b[32m-> \u001b[39m\u001b[32m1443\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43m_retry\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43m_request_once\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mhttp_request\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mstream\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/lib/python3.13/site-packages/tenacity/__init__.py:470\u001b[39m, in \u001b[36mRetrying.__call__\u001b[39m\u001b[34m(self, fn, *args, **kwargs)\u001b[39m\n\u001b[32m    468\u001b[39m retry_state = RetryCallState(retry_object=\u001b[38;5;28mself\u001b[39m, fn=fn, args=args, kwargs=kwargs)\n\u001b[32m    469\u001b[39m \u001b[38;5;28;01mwhile\u001b[39;00m \u001b[38;5;28;01mTrue\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m470\u001b[39m     do = \u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43miter\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43mretry_state\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mretry_state\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n\u001b[32m    471\u001b[39m     \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(do, DoAttempt):\n\u001b[32m    472\u001b[39m         \u001b[38;5;28;01mtry\u001b[39;00m:\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/lib/python3.13/site-packages/tenacity/__init__.py:371\u001b[39m, in \u001b[36mBaseRetrying.iter\u001b[39m\u001b[34m(self, retry_state)\u001b[39m\n\u001b[32m    369\u001b[39m result = \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m    370\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m action \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m.iter_state.actions:\n\u001b[32m--> \u001b[39m\u001b[32m371\u001b[39m     result = \u001b[30;43maction\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43mretry_state\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n\u001b[32m    372\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m result\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/lib/python3.13/site-packages/tenacity/__init__.py:413\u001b[39m, in \u001b[36mBaseRetrying._post_stop_check_actions.<locals>.exc_check\u001b[39m\u001b[34m(rs)\u001b[39m\n\u001b[32m    411\u001b[39m retry_exc = \u001b[38;5;28mself\u001b[39m.retry_error_cls(fut)\n\u001b[32m    412\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m.reraise:\n\u001b[32m--> \u001b[39m\u001b[32m413\u001b[39m     \u001b[38;5;28;01mraise\u001b[39;00m \u001b[30;43mretry_exc\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mreraise\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n\u001b[32m    414\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m retry_exc \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mfut\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mexception\u001b[39;00m()\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/lib/python3.13/site-packages/tenacity/__init__.py:184\u001b[39m, in \u001b[36mRetryError.reraise\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m    182\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mreraise\u001b[39m(\u001b[38;5;28mself\u001b[39m) -> t.NoReturn:\n\u001b[32m    183\u001b[39m     \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m.last_attempt.failed:\n\u001b[32m--> \u001b[39m\u001b[32m184\u001b[39m         \u001b[38;5;28;01mraise\u001b[39;00m \u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mlast_attempt\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mresult\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n\u001b[32m    185\u001b[39m     \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;28mself\u001b[39m\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/lib/python3.13/concurrent/futures/_base.py:449\u001b[39m, in \u001b[36mFuture.result\u001b[39m\u001b[34m(self, timeout)\u001b[39m\n\u001b[32m    447\u001b[39m     \u001b[38;5;28;01mraise\u001b[39;00m CancelledError()\n\u001b[32m    448\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28mself\u001b[39m._state == FINISHED:\n\u001b[32m--> \u001b[39m\u001b[32m449\u001b[39m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43m__get_result\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n\u001b[32m    451\u001b[39m \u001b[38;5;28mself\u001b[39m._condition.wait(timeout)\n\u001b[32m    453\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m._state \u001b[38;5;129;01min\u001b[39;00m [CANCELLED, CANCELLED_AND_NOTIFIED]:\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/lib/python3.13/concurrent/futures/_base.py:401\u001b[39m, in \u001b[36mFuture.__get_result\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m    399\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m._exception \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m    400\u001b[39m     \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m401\u001b[39m         \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;28mself\u001b[39m._exception\n\u001b[32m    402\u001b[39m     \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[32m    403\u001b[39m         \u001b[38;5;66;03m# Break a reference cycle with the exception in self._exception\u001b[39;00m\n\u001b[32m    404\u001b[39m         \u001b[38;5;28mself\u001b[39m = \u001b[38;5;28;01mNone\u001b[39;00m\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/lib/python3.13/site-packages/tenacity/__init__.py:473\u001b[39m, in \u001b[36mRetrying.__call__\u001b[39m\u001b[34m(self, fn, *args, **kwargs)\u001b[39m\n\u001b[32m    471\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(do, DoAttempt):\n\u001b[32m    472\u001b[39m     \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m473\u001b[39m         result = \u001b[30;43mfn\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43m*\u001b[39;49m\u001b[30;43margs\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43m*\u001b[39;49m\u001b[30;43m*\u001b[39;49m\u001b[30;43mkwargs\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n\u001b[32m    474\u001b[39m     \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m:  \u001b[38;5;66;03m# noqa: B902\u001b[39;00m\n\u001b[32m    475\u001b[39m         retry_state.set_exception(sys.exc_info())  \u001b[38;5;66;03m# type: ignore[arg-type]\u001b[39;00m\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/lib/python3.13/site-packages/google/genai/_api_client.py:1420\u001b[39m, in \u001b[36mBaseApiClient._request_once\u001b[39m\u001b[34m(self, http_request, stream)\u001b[39m\n\u001b[32m   1412\u001b[39m   httpx_request = \u001b[38;5;28mself\u001b[39m._httpx_client.build_request(  \u001b[38;5;66;03m# type: ignore[union-attr]\u001b[39;00m\n\u001b[32m   1413\u001b[39m       method=http_request.method,\n\u001b[32m   1414\u001b[39m       url=http_request.url,\n\u001b[32m   (...)\u001b[39m\u001b[32m   1417\u001b[39m       timeout=http_request.timeout,\n\u001b[32m   1418\u001b[39m   )\n\u001b[32m   1419\u001b[39m   response = \u001b[38;5;28mself\u001b[39m._httpx_client.send(httpx_request, stream=stream)  \u001b[38;5;66;03m# type: ignore[union-attr]\u001b[39;00m\n\u001b[32m-> \u001b[39m\u001b[32m1420\u001b[39m \u001b[30;43merrors\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mAPIError\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mraise_for_response\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43mresponse\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n\u001b[32m   1421\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m HttpResponse(\n\u001b[32m   1422\u001b[39m     response.headers, response \u001b[38;5;28;01mif\u001b[39;00m stream \u001b[38;5;28;01melse\u001b[39;00m [response.text]\n\u001b[32m   1423\u001b[39m )\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/lib/python3.13/site-packages/google/genai/errors.py:155\u001b[39m, in \u001b[36mAPIError.raise_for_response\u001b[39m\u001b[34m(cls, response)\u001b[39m\n\u001b[32m    152\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m    153\u001b[39m   response_json = response.body_segments[\u001b[32m0\u001b[39m].get(\u001b[33m'\u001b[39m\u001b[33merror\u001b[39m\u001b[33m'\u001b[39m, {})\n\u001b[32m--> \u001b[39m\u001b[32m155\u001b[39m \u001b[30;43mcls\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mraise_error\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43mresponse\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mstatus_code\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mresponse_json\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mresponse\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/lib/python3.13/site-packages/google/genai/errors.py:186\u001b[39m, in \u001b[36mAPIError.raise_error\u001b[39m\u001b[34m(cls, status_code, response_json, response)\u001b[39m\n\u001b[32m    184\u001b[39m   \u001b[38;5;28;01mraise\u001b[39;00m ClientError(status_code, response_json, response)\n\u001b[32m    185\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m \u001b[32m500\u001b[39m <= status_code < \u001b[32m600\u001b[39m:\n\u001b[32m--> \u001b[39m\u001b[32m186\u001b[39m   \u001b[38;5;28;01mraise\u001b[39;00m ServerError(status_code, response_json, response)\n\u001b[32m    187\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m    188\u001b[39m   \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;28mcls\u001b[39m(status_code, response_json, response)\n",
      "\u001b[31mServerError\u001b[39m: 503 UNAVAILABLE. {'error': {'code': 503, 'message': 'This model is currently experiencing high demand. Spikes in demand are usually temporary. Please try again later.', 'status': 'UNAVAILABLE'}}"
     ]
    }
   ],
   "source": [
    "import os\n",
    "import time\n",
    "import base64 \n",
    "from google import genai\n",
    "from google.genai.errors import APIError\n",
    "\n",
    "os.environ[\"GEMINI_API_KEY\"] = \"AQ.Ab8RN6K9G5S-h6IWgpUgfmHbIk7b8iZlWTuYptMFqOT4Ph6FnQ\"\n",
    "client = genai.Client()\n",
    "\n",
    "prompt_instructions_detailed5 = (\n",
    "    \"You are processing raw course text meant for Jupyter Notebook markdown cells.\\n\\n\"\n",
    "    \n",
    "    \"CRITICAL RULE: Do NOT delete, alter, or remove any original image links, image paths, \"\n",
    "    \"or image tags (e.g., lines starting with `![...](...)` or containing `<img ...>`). \"\n",
    "    \"Leave them exactly where they are in the original text sequence.\\n\\n\"\n",
    "    \n",
    "    \"INSTRUCTION:\\n\"\n",
    "    \"Rewrite the text to make it clear and university-appropriate. Make it clear and write WCAG 2.1 AA compliant. Use the following strict sequence \"\n",
    "    \"when dealing with image placements:\\n\\n\"\n",
    "    \n",
    "    \"1. First, insert the NEW Text-Based Structured Alternative ABOVE the image:\\n\"\n",
    "    \"   - For numerical data charts or grid plots: Provide a clean Markdown Data Table.\\n\"\n",
    "    \"   - For hardware architecture, network layouts, or OS Process lifecycles (like fork(), process states, execution threads): \"\n",
    "    \"Provide a high-level text flowchart inside a preformatted ```text code block.\\n\"\n",
    "    \"   - For Terminal Commands or Pipelines (like stdin/stdout piping, shell interactions): \"\n",
    "    \"Provide a vertical box-and-arrow sequence map illustrating data flow from one utility to the next.\\n\"\n",
    "    \"   - Keep diagram characters perfectly aligned and compatible with static HTML page exports.\\n\\n\"\n",
    "    \"   - CRITICAL RULE FOR VIRTUAL DESKTOP LOGIN CONTEXT:\\n\"\n",
    "    \"     If the text or diagram is about logging into the Virtual Desktop or using Ravada, \"\n",
    "    \"the text diagram sequence MUST start exactly with this workflow path:\\n\"\n",
    "    \"     [Web access to Ravada portal with authentication] ===> [Start VM] ===> [Access VM via SSH with provided port]\\n\\n\"\n",
    "    \n",
    "    \"2. Second, place the ORIGINAL untouched image link exactly as it was.\\n\\n\"\n",
    "    \n",
    "    \"3. Third, immediately BELOW the original image link, add the Comprehensive Detailed Description:\\n\"\n",
    "    \"Inside this caption block, you MUST start with the exact text '**Accessible Image Description:**'. Place it always under the image.\"\n",
    "    \"   - Overview: One sentence describing what the visual shows at a high level.\\n\"\n",
    "    \"   - Setup: Explain the primary components, labels, axes, or code pathways without hyper-granular specs.\\n\"\n",
    "    \"   - Key Takeaways: Core conceptual trends or execution results shown.\\n\\n\"\n",
    "\n",
    "    f\"Course Text:\\n{my_notebook_text}\"\n",
    ")\n",
    "prompt_instructions = prompt_instructions_detailed5\n",
    "\n",
    "try:\n",
    "    response = client.models.generate_content(\n",
    "        model='models/gemini-3.6-flash',\n",
    "        contents= prompt_instructions,\n",
    "        )\n",
    "\n",
    "    #print(response.text)\n",
    "\n",
    "    output_text = response.text\n",
    "    print(output_text)\n",
    "    \n",
    "except APIError as e:\n",
    "    raise e"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "# Package Management\n",
    "\n",
    "## Course Outline\n",
    "\n",
    "- Discussion of software installation workflows in Linux.\n",
    "- Debian and Ubuntu package structure and management.\n",
    "- Advanced Package Tool (`apt`) for package installation, removal, searching, and querying.\n",
    "- Debian package low-level tool (`dpkg`) for direct package installation, removal, and querying.\n",
    "- Red Hat Enterprise Linux (RHEL) package structure and management.\n",
    "- Dandified (`dnf`), a yellowdog Updater, Modified (`yum`) for package installation, removal, searching, and querying.\n",
    "- Red Hat Package Manager (`rpm`) for direct package installation, removal, and querying.\n",
    "\n",
    "***\n",
    "\n",
    "## The Purpose of Linux Packages\n",
    "\n",
    "In modern Linux distributions, software is compiled, structured, and distributed as compressed archives known as **packages**. These packages are hosted on remote repository servers (mirrors). \n",
    "\n",
    "Linux package managers automate and maintain the following operational tasks:\n",
    "- **Installing software:** Fetching software components and placing binaries, libraries, and configuration files in standard filesystem directories.\n",
    "- **Removing software:** Safely deleting application files while preserving system stability.\n",
    "- **Updating software:** Upgrading installed applications and libraries to newer versions.\n",
    "- **Repairing software:** Fixing missing, damaged, or partially removed program files.\n",
    "- **Reconfiguring software:** Adjusting post-installation parameters and default configurations.\n",
    "- **Verifying integrity:** Validating installed files against cryptographic hashes to detect file corruption or unauthorized modification.\n",
    "\n",
    "***\n",
    "\n",
    "## Package Formats and Tools Across Linux Distributions\n",
    "\n",
    "Different distribution families utilize specific packaging formats and package management utility stacks:\n",
    "\n",
    "- **Debian / Ubuntu:** Package extension `.deb` | High-level tool: `apt` | Low-level tool: `dpkg`\n",
    "- **Red Hat / Rocky / Fedora:** Package extension `.rpm` | High-level tool: `dnf` (or `yum`) | Low-level tool: `rpm`\n",
    "- **Arch / Manjaro:** Package extension `.pkg.tar.zst` | Package manager: `pacman`\n",
    "- **Gentoo:** Source-based compilation management | Package manager: `portage` (`emerge`)\n",
    "\n",
    "### Modern Application Packaging Utilities\n",
    "\n",
    "In addition to traditional system distribution packages, containerized and application-centric packaging frameworks exist:\n",
    "- **Frameworks:** `snap` (Ubuntu) and `Flatpak` (Fedora, universal Linux desktop distribution).\n",
    "- **Sandboxing:** Applications run in isolated user-space environments.\n",
    "- **Self-contained Dependencies:** All required runtime libraries are bundled inside the package, eliminating distribution-level dependency conflicts.\n",
    "- **Use Case:** Primarily targeted at desktop applications, utility tools, and specific third-party service deployments.\n",
    "\n",
    "***\n",
    "\n",
    "## Linux Package Management Workflows\n",
    "\n",
    "```text\n",
    "+-----------------------------------------------------------------------------------+\n",
    "|                           LINUX PACKAGE MANAGEMENT FLOW                           |\n",
    "+-----------------------------------------------------------------------------------+\n",
    "|                                                                                   |\n",
    "|  [ Remote Repository ] <=================================> [ Remote Repository ]  |\n",
    "|           |                                                         |             |\n",
    "|           | High-Level (Resolves Dependencies)                      | High-Level  |\n",
    "|           v                                                         v             |\n",
    "|      +----------+                                              +----------+       |\n",
    "|      |   APT    | (Debian / Ubuntu)                            | DNF/YUM  | (RedHat)|\n",
    "|      +----------+                                              +----------+       |\n",
    "|           |                                                         |             |\n",
    "|           v Downloads .deb                                          v Downloads .rpm\n",
    "|    +--------------+                                           +--------------+    |\n",
    "|    | Local .deb   |                                           | Local .rpm   |    |\n",
    "|    | Package File |                                           | Package File |    |\n",
    "|    +--------------+                                           +--------------+    |\n",
    "|           |                                                         |             |\n",
    "|           | Low-Level (Direct Installation)                         | Low-Level   |\n",
    "|           v                                                         v             |\n",
    "|      +----------+                                              +----------+       |\n",
    "|      |   dpkg    | (Debian low-level: dpkg)                    |   rpm    |       |\n",
    "|      +----------+                                              +----------+       |\n",
    "|           |                                                         |             |\n",
    "|           +---------------------------+-----------------------------+             |\n",
    "|                                       |                                           |\n",
    "|                                       v                                           |\n",
    "|                          [ Installed System Files ]                               |\n",
    "|                          (/usr/bin, /etc, /lib, etc.)                             |\n",
    "|                                                                                   |\n",
    "+-----------------------------------------------------------------------------------+\n",
    "```\n",
    "\n",
    "```{figure} img/packagemanagers.png\n",
    "---\n",
    "height: 600px\n",
    "name: Debian_Redhat_pkg\n",
    "---\n",
    "```\n",
    "\n",
    "**Accessible Image Description:**\n",
    "- **Overview:** Visual workflow illustrating how high-level package tools (`apt` and `dnf`) and low-level package installers (`dpkg` and `rpm`) interact with package archives (`.deb` and `.rpm`) to install software components onto Debian/Ubuntu and Red Hat-based Linux systems.\n",
    "- **Setup:** Two parallel processing pipelines are shown side by side. On the left, Debian/Ubuntu workflows query remote repositories using `apt`, acquire `.deb` files, and execute underlying installations via `dpkg`. On the right, Red Hat workflows query remote repositories using `dnf`/`yum`, acquire `.rpm` files, and execute installations via `rpm`.\n",
    "- **Key Takeaways:** High-level package managers streamline package administration by automatically fetching software from remote networks and resolving prerequisites. Low-level utilities manage local archive files directly on the host system without handling network resolution.\n",
    "\n",
    "***\n",
    "\n",
    "## Debian and Ubuntu Packages\n",
    "\n",
    "Operating system components and software packages in Ubuntu/Debian are distributed as `.deb` archives.\n",
    "\n",
    "A `.deb` package contains:\n",
    "1. **Executable Application Data:** Program binaries, documentation, and libraries.\n",
    "2. **Control Information (`control` file):** Package metadata, vendor information, architecture constraints, and explicit dependency lists.\n",
    "3. **Maintainer Scripts:** Pre-installation (`preinst`), post-installation (`postinst`), pre-removal (`prerm`), and post-removal (`postrm`) shell scripts.\n",
    "4. **Hashes (`md5, sha1, sha256, sha512`):** Cryptographic checksums used to verify the integrity of installed files.\n",
    "\n",
    "### Package Naming Convention\n",
    "\n",
    "```text\n",
    "(package-name)_(source-version)-(package-revision)_(architecture).deb\n",
    "```\n",
    "\n",
    "*Example:* `make_4.4.1-3_amd64.deb`\n",
    "- **Package name:** `make`\n",
    "- **Source version:** `4.4.1` (GNU Make version)\n",
    "- **Package revision:** `3` (Debian/Ubuntu distribution build iteration)\n",
    "- **Target architecture:** `amd64` (64-bit x86 platform)\n",
    "\n",
    "### Ubuntu Repository Sections\n",
    "\n",
    "Official Ubuntu package archives are organized into distinct repositories based on software freedom and support status:\n",
    "\n",
    "- **`main`:** Free and open-source software officially supported and maintained by Canonical.\n",
    "- **`restricted`:** Proprietary drivers and software essential for device functionality (e.g., `nvidia-driver-595`).\n",
    "- **`universe`:** Community-maintained open-source software. Updates and security fixes are provided by community volunteers rather than official distribution maintainers (e.g., `a2ps`, `xemacs21`).\n",
    "- **`multiverse`:** Software restricted by copyright or legal licensing issues (e.g., `dvd-slideshow`).\n",
    "\n",
    "Developers and third-party vendors may also offer custom package repositories known as **Personal Package Archives (PPAs)**.\n",
    "\n",
    "### Debian Repository Sections\n",
    "\n",
    "Debian repositories categorize software under the following branches:\n",
    "- `main`: Fully compliant open-source software.\n",
    "- `contrib`: Open-source software that depends on packages outside of `main`.\n",
    "- `non-free`: Software with restrictive license conditions.\n",
    "- `non-free-firmware`: Hardware firmware blobs with non-open licensing.\n",
    "\n",
    "***\n",
    "\n",
    "## Hands-on Exercise: Deploying the `kvm3` Virtual Machine\n",
    "\n",
    "1. Shutdown `kvm1` and clone it to create `kvm3`:\n",
    "   ```bash\n",
    "   virsh shutdown kvm1\n",
    "   virt-clone -o kvm1 -n kvm3 -f /home/hostadm/KVM/kvm3.qcow2\n",
    "   ```\n",
    "\n",
    "2. Start the `kvm3` virtual machine and open its console via `virsh`:\n",
    "   ```bash\n",
    "   virsh start kvm3\n",
    "   virsh console kvm3\n",
    "   ```\n",
    "\n",
    "3. Update the system hostname inside `/etc/hostname`:\n",
    "   ```bash\n",
    "   sudo -s\n",
    "   nano /etc/hostname\n",
    "   ```\n",
    "   *Replace `kvm1` with `kvm3`.*\n",
    "\n",
    "4. Reset unique system identification parameters by running the resetting script:\n",
    "   ```bash\n",
    "   vm_id_reset.sh\n",
    "   ```\n",
    "\n",
    "5. Reboot the virtual machine:\n",
    "   ```bash\n",
    "   reboot\n",
    "   ```\n",
    "\n",
    "***\n",
    "\n",
    "## Hands-on Exercise: Installing Ubuntu Packages with APT\n",
    "\n",
    "1. Log into the `kvm3` instance.\n",
    "\n",
    "2. Attempt to execute the `make` utility to verify its presence:\n",
    "   ```bash\n",
    "   make\n",
    "   ```\n",
    "   *System output:* `The program 'make' is currently not installed.`\n",
    "\n",
    "3. Install the `make` utility using APT:\n",
    "   ```bash\n",
    "   apt install make\n",
    "   ```\n",
    "   *Verify that `make` executes successfully following installation.*\n",
    "\n",
    "4. Perform a simulated installation of a package using the `-s` (dry-run) option:\n",
    "   ```bash\n",
    "   apt install -s netpbm\n",
    "   ```\n",
    "   *Note the prerequisite dependency library `libnetpbm10` scheduled for automated installation.*\n",
    "\n",
    "5. Download the `.deb` package file into the local APT cache without installing it:\n",
    "   ```bash\n",
    "   apt install -d netpbm\n",
    "   ```\n",
    "\n",
    "6. Inspect the downloaded package file in the APT cache directory:\n",
    "   ```bash\n",
    "   ls -l /var/cache/apt/archives\n",
    "   ```\n",
    "\n",
    "7. Download a package directly into your current working directory:\n",
    "   ```bash\n",
    "   apt download netpbm\n",
    "   ```\n",
    "\n",
    "8. Install the package:\n",
    "   ```bash\n",
    "   apt install netpbm\n",
    "   ```\n",
    "\n",
    "9. Synchronize the local repository index and upgrade installed packages:\n",
    "   ```bash\n",
    "   apt update\n",
    "   apt upgrade\n",
    "   ```\n",
    "\n",
    "*Note:* APT repository configuration and URL references are stored in `/etc/apt/sources.list` and within modular files in `/etc/apt/sources.list.d/`.\n",
    "\n",
    "***\n",
    "\n",
    "## Hands-on Exercise: Removing Ubuntu Packages with APT\n",
    "\n",
    "1. Remove the `make` package:\n",
    "   ```bash\n",
    "   apt remove make\n",
    "   ```\n",
    "\n",
    "2. Simulate package removal using the `-s` dry-run parameter:\n",
    "   ```bash\n",
    "   apt remove -s netpbm\n",
    "   ```\n",
    "   *Observe that the dependent library `libnetpbm10` remains on the system.*\n",
    "\n",
    "3. Simulate removing a package along with all orphaned dependencies:\n",
    "   ```bash\n",
    "   apt autoremove -s netpbm\n",
    "   ```\n",
    "   *Observe that both `netpbm` and `libnetpbm10` are scheduled for removal.*\n",
    "\n",
    "4. Perform the automated removal of the package and its unneeded dependencies:\n",
    "   ```bash\n",
    "   apt autoremove netpbm\n",
    "   ```\n",
    "\n",
    "***\n",
    "\n",
    "## Hands-on Exercise: Searching and Querying Ubuntu Packages with APT\n",
    "\n",
    "1. Refresh local package index files:\n",
    "   ```bash\n",
    "   apt update\n",
    "   ```\n",
    "\n",
    "2. Search for packages containing the keyword `make` in their name or detailed description:\n",
    "   ```bash\n",
    "   apt-cache search make\n",
    "   ```\n",
    "\n",
    "3. Limit search queries to match package names only:\n",
    "   ```bash\n",
    "   apt-cache search --names-only make\n",
    "   ```\n",
    "\n",
    "4. Filter search results precisely for package names beginning with `make`:\n",
    "   ```bash\n",
    "   apt-cache search --names-only make | grep -w ^make\n",
    "   ```\n",
    "\n",
    "5. Display detailed package metadata for `make`:\n",
    "   ```bash\n",
    "   apt-cache show make\n",
    "   ```\n",
    "\n",
    "6. List packages that rely upon `make` as a dependency:\n",
    "   ```bash\n",
    "   apt-cache showpkg make\n",
    "   ```\n",
    "\n",
    "7. List all direct dependencies required by `make`:\n",
    "   ```bash\n",
    "   apt-cache depends make\n",
    "   ```\n",
    "\n",
    "***\n",
    "\n",
    "## Hands-on Exercise: Querying Repository Components\n",
    "\n",
    "Executing `apt-cache show` reveals the specific repository archive path (`main`, `restricted`, `universe`, or `multiverse`) hosting a package:\n",
    "\n",
    "- Check the repository directory for `make`:\n",
    "  ```bash\n",
    "  apt-cache show make | grep Filename\n",
    "  ```\n",
    "  *Output:* `Filename: pool/main/m/make-dfsg/make_4.4.1-3_amd64.deb`\n",
    "\n",
    "- Check the repository directory for `nvidia-driver-595`:\n",
    "  ```bash\n",
    "  apt-cache show nvidia-driver-595 | grep Filename\n",
    "  ```\n",
    "  *Output:* `Filename: pool/restricted/n/nvidia-graphics-drivers-595/nvidia-driver-595_595.71.05-0ubuntu0.26.04.1_amd64.deb`\n",
    "\n",
    "- Check the repository directory for `a2ps`:\n",
    "  ```bash\n",
    "  apt-cache show a2ps | grep Filename\n",
    "  ```\n",
    "  *Output:* `Filename: pool/universe/a/a2ps/a2ps_4.15.7-5_amd64.deb`\n",
    "\n",
    "- Check the repository directory for `dvd-slideshow`:\n",
    "  ```bash\n",
    "  apt-cache show dvd-slideshow | grep Filename\n",
    "  ```\n",
    "  *Output:* `Filename: pool/multiverse/d/dvd-slideshow/dvd-slideshow_0.8.6.1-2build1_all.deb`\n",
    "\n",
    "***\n",
    "\n",
    "## Hands-on Exercise: APT Security and Cryptographic Verification\n",
    "\n",
    "### APT Verification Principles\n",
    "- Downloaded package files are cached in `/var/cache/apt/archives/` prior to installation.\n",
    "- The repository index file (`Packages`) contains cryptographic hashes (MD5, SHA-1, SHA-256, SHA-512) accessible via `apt-cache show`.\n",
    "- During installation, `apt install` computes the local file hash and matches it against the hash published in `Packages`.\n",
    "- Cached package indices reside in `/var/lib/apt/`.\n",
    "- The `Packages` index file `sha256` checksums are stored in `InRelease` file.\n",
    "- The  `InRelease` metadata file is cryptographically signed using GnuPG (GPG) keys maintained by repository owners.\n",
    "- System keyrings containing trusted public keys are located in `/usr/share/keyrings/` (on modern releases such as Ubuntu 26.04) or historically in `/etc/apt/trusted.gpg.d/`.\n",
    "\n",
    "### GPG Key Verification Test\n",
    "\n",
    "1. Simulate trust key corruption by removing local public keyrings:\n",
    "   ```bash\n",
    "   rm /usr/share/keyrings/*\n",
    "   ```\n",
    "\n",
    "2. Attempt to update repository indices:\n",
    "   ```bash\n",
    "   apt update\n",
    "   ```\n",
    "   *Observe error messages indicating signatures cannot be verified due to missing public keys.*\n",
    "\n",
    "3. Restore the official security keyrings:\n",
    "   ```bash\n",
    "   apt install ubuntu-keyring --reinstall\n",
    "   ```\n",
    "\n",
    "4. Re-verify repository signature authentication:\n",
    "   ```bash\n",
    "   apt update\n",
    "   ```\n",
    "\n",
    "***\n",
    "\n",
    "## Hands-on Exercise: Adding an External APT Repository\n",
    "\n",
    "Custom repository sources can be appended to `/etc/apt/sources.list` or configured as independent files within `/etc/apt/sources.list.d/`.\n",
    "\n",
    "*Example Configuration: Adding the BeeGFS File System Repository*\n",
    "\n",
    "1. Download the repository source file into `/etc/apt/sources.list.d/`:\n",
    "   ```bash\n",
    "   cd /etc/apt/sources.list.d\n",
    "   wget https://www.beegfs.io/release/beegfs_8.3/dists/beegfs-noble.list\n",
    "   ```\n",
    "\n",
    "2. Download, dearmor, and store the vendor GPG public security key:\n",
    "   ```bash\n",
    "   cd /tmp\n",
    "   wget https://www.beegfs.io/release/beegfs_8.3/gpg/GPG-KEY-beegfs\n",
    "   cat GPG-KEY-beegfs | sudo gpg --dearmor -o /etc/apt/keyrings/GPG-KEY-beegfs.gpg\n",
    "   ```\n",
    "\n",
    "3. Ensure `/etc/apt/sources.list.d/beegfs-noble.list` references the stored GPG key ring:\n",
    "   ```text\n",
    "   deb [signed-by=/etc/apt/keyrings/GPG-KEY-beegfs.gpg] https://www.beegfs.io/release/beegfs_8.3 noble non-free\n",
    "   ```\n",
    "\n",
    "4. Update APT package caches:\n",
    "   ```bash\n",
    "   apt update\n",
    "   ```\n",
    "\n",
    "5. Install the software utility from the new repository source:\n",
    "   ```bash\n",
    "   apt install beegfs-tools\n",
    "   ```\n",
    "\n",
    "***\n",
    "\n",
    "## Hands-on Exercise: Installing Specific Package Versions with APT\n",
    "\n",
    "1. Query all repository versions available for a given package:\n",
    "   ```bash\n",
    "   apt-cache show linux-image-virtual\n",
    "   ```\n",
    "\n",
    "2. Install a specific targeted package version using the `=` syntax:\n",
    "   ```bash\n",
    "   apt install linux-image-virtual=7.0.0-31.31\n",
    "   ```\n",
    "\n",
    "***\n",
    "\n",
    "## Reviewing APT Transaction Logs\n",
    "\n",
    "Historical records of all `apt` actions are logged in `/var/log/apt/history.log`.\n",
    "\n",
    "- Interactively review full execution history:\n",
    "  ```bash\n",
    "  less /var/log/apt/history.log\n",
    "  ```\n",
    "\n",
    "- View the most recent transaction entries:\n",
    "  ```bash\n",
    "  tail -20 /var/log/apt/history.log\n",
    "  ```\n",
    "\n",
    "***\n",
    "\n",
    "## Hands-on Exercise: Querying and Managing Packages with `dpkg`\n",
    "\n",
    "1. List all packages currently installed on the local system:\n",
    "   ```bash\n",
    "   dpkg -l\n",
    "   ```\n",
    "\n",
    "2. Query installation status and detailed metadata for specific packages:\n",
    "   ```bash\n",
    "   dpkg -s make\n",
    "   dpkg -s tzdata\n",
    "   ```\n",
    "\n",
    "3. List all system files owned and installed by a package:\n",
    "   ```bash\n",
    "   dpkg -L tzdata\n",
    "   ```\n",
    "\n",
    "4. Identify which package installed a specific system file (e.g., `/bin/ls`):\n",
    "   ```bash\n",
    "   dpkg -S /bin/ls\n",
    "   ```\n",
    "\n",
    "5. Reconfigure post-installation settings for an installed package:\n",
    "   ```bash\n",
    "   dpkg-reconfigure tzdata\n",
    "   ```\n",
    "\n",
    "6. Inspect configured settings maintained by `debconf`:\n",
    "   ```bash\n",
    "   debconf-show tzdata\n",
    "   ```\n",
    "\n",
    "7. Direct manual installation of local `.deb` files using `dpkg`:\n",
    "   ```bash\n",
    "   apt download make\n",
    "   dpkg -i make_4.4.1-3_amd64.deb\n",
    "   ```\n",
    "\n",
    "8. List target files contained within an uninstalled `.deb` package archive:\n",
    "   ```bash\n",
    "   dpkg --contents make_4.4.1-3_amd64.deb\n",
    "   ```\n",
    "\n",
    "9. Verify installed package file integrity against original checksums:\n",
    "   ```bash\n",
    "   dpkg -V make\n",
    "   ```\n",
    "   *(A clean check produces no console output).*\n",
    "\n",
    "10. Tamper with installed files to evaluate verification output:\n",
    "    ```bash\n",
    "    echo '/* modified line */' >> /usr/include/gnumake.h\n",
    "    rm /usr/share/man/man1/make.1.gz\n",
    "    ```\n",
    "\n",
    "11. Re-run integrity verification to observe altered (`M`) and missing (`missing`) files:\n",
    "    ```bash\n",
    "    dpkg -V make\n",
    "    ```\n",
    "\n",
    "12. Purge package binaries and configuration files:\n",
    "    ```bash\n",
    "    dpkg --purge make\n",
    "    ```\n",
    "\n",
    "13. Extract `.deb` contents into an uncompressed directory structure:\n",
    "    ```bash\n",
    "    dpkg -X make_4.4.1-3_amd64.deb PKGdir\n",
    "    ```\n",
    "\n",
    "14. View extracted directory hierarchy using `tree`:\n",
    "    ```bash\n",
    "    apt install tree\n",
    "    tree PKGdir\n",
    "    ```\n",
    "\n",
    "***\n",
    "\n",
    "## Red Hat Packages\n",
    "\n",
    "Packages within Red Hat Enterprise Linux (RHEL), Rocky Linux, and Fedora follow standard RPM naming protocols:\n",
    "\n",
    "```text\n",
    "(package-name)-(source-version)-(package-release).(architecture).rpm\n",
    "```\n",
    "\n",
    "*Example:* `nano-8.1-3.el10.x86_64.rpm`\n",
    "- **Package name:** `nano`\n",
    "- **Source version:** `8.1`\n",
    "- **Release iteration:** `3.el10` (Build iteration 3 for Enterprise Linux 10)\n",
    "- **Target architecture:** `x86_64` (64-bit AMD/Intel architecture)\n",
    "\n",
    "***\n",
    "\n",
    "## Hands-on Exercise: Deploying the `rocky10-1` Virtual Machine\n",
    "\n",
    "1. Clone `rocky10` into a new instance named `rocky10-1`:\n",
    "   ```bash\n",
    "   virt-clone -o rocky10 -n rocky10-1 -f /home/hostadm/KVM/rocky10-1.qcow2\n",
    "   ```\n",
    "\n",
    "2. Boot `rocky10-1` and connect to its virtual console:\n",
    "   ```bash\n",
    "   virsh start rocky10-1\n",
    "   virsh console rocky10-1\n",
    "   ```\n",
    "\n",
    "3. Update the hostname in `/etc/hostname` to `rocky10-1`.\n",
    "\n",
    "4. Reset machine identification parameters and flush active networking leases:\n",
    "   ```bash\n",
    "   sudo dnf install dbus-tools\n",
    "   sudo rm -f /etc/machine-id\n",
    "   sudo dbus-uuidgen --ensure=/etc/machine-id\n",
    "   sudo dbus-uuidgen --ensure\n",
    "   sudo ip addr flush dev enp1s0\n",
    "   ```\n",
    "\n",
    "5. Reboot the virtual machine:\n",
    "   ```bash\n",
    "   sudo reboot\n",
    "   ```\n",
    "\n",
    "***\n",
    "\n",
    "## Hands-on Exercise: Package Management using DNF\n",
    "\n",
    "Dandified YUM (DNF) serves as the primary high-level package manager for modern RPM-based distributions.\n",
    "\n",
    "1. Clear cached repository metadata:\n",
    "   ```bash\n",
    "   dnf clean all\n",
    "   ```\n",
    "\n",
    "2. Install a package:\n",
    "   ```bash\n",
    "   dnf install netpbm\n",
    "   ```\n",
    "\n",
    "3. Reinstall a package to repair damaged binaries:\n",
    "   ```bash\n",
    "   dnf reinstall netpbm\n",
    "   ```\n",
    "\n",
    "4. Download an RPM package without running installation routines:\n",
    "   ```bash\n",
    "   dnf download make\n",
    "   ls -l make*\n",
    "   ```\n",
    "\n",
    "5. Check for available package updates across enabled repositories:\n",
    "   ```bash\n",
    "   dnf check-update\n",
    "   ```\n",
    "\n",
    "6. Update a single specified package:\n",
    "   ```bash\n",
    "   dnf update tzdata\n",
    "   ```\n",
    "\n",
    "7. Perform a full system package upgrade:\n",
    "   ```bash\n",
    "   dnf update\n",
    "   ```\n",
    "\n",
    "8. Remove installed software packages:\n",
    "   ```bash\n",
    "   dnf remove netpbm\n",
    "   ```\n",
    "   *(Note: `dnf erase netpbm` yields identical removal results).*\n",
    "\n",
    "***\n",
    "\n",
    "## Hands-on Exercise: Searching and Querying Packages with DNF\n",
    "\n",
    "1. Inspect package installation status:\n",
    "   ```bash\n",
    "   dnf list make\n",
    "   dnf list coreutils\n",
    "   ```\n",
    "   *Note: Output displays package architecture, version, and source repository. Repository names prefixed with `@` indicate installed packages.*\n",
    "\n",
    "2. Display all installed system packages:\n",
    "   ```bash\n",
    "   dnf list --installed\n",
    "   dnf repoquery --installed\n",
    "   ```\n",
    "\n",
    "3. Display packages available in configured repositories:\n",
    "   ```bash\n",
    "   dnf list --available\n",
    "   dnf repoquery\n",
    "   ```\n",
    "\n",
    "4. Search repository catalog entries by keyword:\n",
    "   ```bash\n",
    "   dnf search make\n",
    "   ```\n",
    "\n",
    "5. Query detailed metadata for a package:\n",
    "   ```bash\n",
    "   dnf info make\n",
    "   ```\n",
    "\n",
    "6. List all software dependencies required by a target package:\n",
    "   ```bash\n",
    "   dnf deplist make\n",
    "   ```\n",
    "\n",
    "7. Determine which package provides a specific system binary or file path:\n",
    "   ```bash\n",
    "   dnf provides /usr/bin/ls\n",
    "   ```\n",
    "\n",
    "***\n",
    "\n",
    "## Hands-on Exercise: Managing DNF Repositories\n",
    "\n",
    "Repository configuration files are stored as `.repo` files in `/etc/yum.repos.d/`.\n",
    "\n",
    "1. Display active (enabled) repositories:\n",
    "   ```bash\n",
    "   dnf repolist\n",
    "   ```\n",
    "   *Standard default repos include `BaseOS`, `AppStream`, and `Extras`.*\n",
    "\n",
    "2. Display inactive (disabled) repositories:\n",
    "   ```bash\n",
    "   dnf repolist --disabled\n",
    "   ```\n",
    "\n",
    "3. Enable a disabled repository source, ```crb``` (CodeReady Builder):\n",
    "   ```bash\n",
    "   dnf config-manager --enable crb\n",
    "   ```\n",
    "\n",
    "4. List packages hosted within a specific repository channel:\n",
    "   ```bash\n",
    "   dnf list --repo crb\n",
    "   ```\n",
    "\n",
    "5. Add a third-party vendor repository file directly from a remote URL:\n",
    "   ```bash\n",
    "   dnf config-manager --add-repo https://www.beegfs.io/release/beegfs_8.3/dists/beegfs-rhel10.repo\n",
    "   ```\n",
    "\n",
    "6. Import vendor public cryptographic signing keys:\n",
    "   ```bash\n",
    "   rpm --import https://www.beegfs.io/release/beegfs_8.3/gpg/GPG-KEY-beegfs\n",
    "   ```\n",
    "\n",
    "7. Install software from the newly configured repository source:\n",
    "   ```bash\n",
    "   dnf install beegfs-tools\n",
    "   ```\n",
    "\n",
    "8. Disable a repository channel:\n",
    "   ```bash\n",
    "   dnf config-manager --disable beegfs\n",
    "   ```\n",
    "\n",
    "9. Remove a custom repository configuration permanently:\n",
    "   ```bash\n",
    "   rm /etc/yum.repos.d/beegfs-rhel10.repo\n",
    "   ```\n",
    "\n",
    "***\n",
    "\n",
    "## Advanced DNF Functionalities\n",
    "\n",
    "1. Review transaction history records:\n",
    "   ```bash\n",
    "   dnf history\n",
    "   ```\n",
    "\n",
    "2. Undo a specific prior transaction ID (e.g., transaction 10):\n",
    "   ```bash\n",
    "   dnf history undo 10\n",
    "   ```\n",
    "\n",
    "3. Roll back system software state to a target transaction point (e.g., transaction 9):\n",
    "   ```bash\n",
    "   dnf history rollback 9\n",
    "   ```\n",
    "\n",
    "4. Inspect aggregate package collections arranged by operational groups:\n",
    "   ```bash\n",
    "   dnf group list\n",
    "   ```\n",
    "\n",
    "*For comprehensive technical references, consult the [Red Hat Managing Software with DNF Documentation](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/pdf/managing_software_with_the_dnf_tool/red_hat_enterprise_linux-9-managing_software_with_the_dnf_tool-en-us.pdf).*\n",
    "\n",
    "***\n",
    "\n",
    "## Hands-on Exercise: Querying and Managing Packages with `rpm`\n",
    "\n",
    "1. List all RPM packages installed on the system:\n",
    "   ```bash\n",
    "   rpm -qa | sort | less\n",
    "   ```\n",
    "\n",
    "2. Direct manual installation of a local `.rpm` package file:\n",
    "   ```bash\n",
    "   rpm -ivh make-4.4.1-9.el10.x86_64.rpm\n",
    "   ```\n",
    "   *-i: install, -v: verbose output, -h: print hash marks showing progress.*\n",
    "\n",
    "3. Verify installation status of a package:\n",
    "   ```bash\n",
    "   rpm -q make\n",
    "   ```\n",
    "\n",
    "4. Query package descriptive information:\n",
    "   ```bash\n",
    "   rpm -qi make\n",
    "   ```\n",
    "\n",
    "5. Query all files installed on the system by a package:\n",
    "   ```bash\n",
    "   rpm -ql make\n",
    "   ```\n",
    "\n",
    "6. Query which RPM package owns a specific system binary:\n",
    "   ```bash\n",
    "   rpm -qf /bin/ls\n",
    "   ```\n",
    "\n",
    "7. List explicit capability requirements for a package:\n",
    "   ```bash\n",
    "   rpm -qR make\n",
    "   ```\n",
    "\n",
    "8. Check integrity of installed package components:\n",
    "   ```bash\n",
    "   rpm -V make\n",
    "   ```\n",
    "\n",
    "9. Modify installed files to test verification warnings:\n",
    "   ```bash\n",
    "   echo '/* modified line */' >> /usr/include/gnumake.h\n",
    "   rm /usr/share/man/man1/make.1.gz\n",
    "   rpm -V make\n",
    "   ```\n",
    "\n",
    "10. Remove an installed RPM package directly:\n",
    "    ```bash\n",
    "    rpm -e make\n",
    "    ```\n",
    "\n",
    "11. Query uninstalled local RPM archive files directly using `-p`:\n",
    "    ```bash\n",
    "    rpm -qp make-4.4.1-9.el10.x86_64.rpm\n",
    "    rpm -qlp make-4.4.1-9.el10.x86_64.rpm\n",
    "    rpm -qip make-4.4.1-9.el10.x86_64.rpm\n",
    "    ```\n",
    "\n",
    "***\n",
    "\n",
    "## Additional Reference Manuals\n",
    "\n",
    "- [Ubuntu Community Rosetta Stone: Package Management for RPM and DEB](https://help.ubuntu.com/community/SwitchingToUbuntu/FromLinux/RedHatEnterpriseLinuxAndFedora)\n",
    "- [IBM Developer: Debian Package Management Fundamentals](https://developer.ibm.com/tutorials/l-lpic1-102-4/)\n",
    "- [IBM Developer: RPM and YUM Package Management Fundamentals](https://developer.ibm.com/tutorials/l-lpic1-102-5/)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
