Skip to content

Commit

Permalink
modularity support
Browse files Browse the repository at this point in the history
  • Loading branch information
furlongm committed May 1, 2023
1 parent cb2fc28 commit 9c46877
Showing 26 changed files with 728 additions and 36 deletions.
3 changes: 2 additions & 1 deletion TODO
Original file line number Diff line number Diff line change
@@ -11,4 +11,5 @@
* helper script to change paths (e.g. /usr/lib/python3/dist-packages/patchman)
* Dockerfile/Dockerimage
* compressed reports
* modularity support
* add cronjobs to built packages
* install celery/rabbit/memcache with packages
73 changes: 71 additions & 2 deletions client/patchman-client
Original file line number Diff line number Diff line change
@@ -71,17 +71,20 @@ cleanup() {
echo "Debug: not deleting ${tmpfile_rep} (repos)"
echo "Debug: not deleting ${tmpfile_sec} (security updates)"
echo "Debug: not deleting ${tmpfile_bug} (updates)"
echo "Debug: not deleting ${tmpfile_mod} (modules)"
elif ${verbose} && ! ${debug} ; then
echo "Deleting ${tmpfile_pkg}"
echo "Deleting ${tmpfile_rep}"
echo "Deleting ${tmpfile_sec}"
echo "Deleting ${tmpfile_bug}"
echo "Deleting ${tmpfile_mod}"
fi
if ! ${debug} ; then
rm -fr "${tmpfile_pkg}"
rm -fr "${tmpfile_rep}"
rm -fr "${tmpfile_sec}"
rm -fr "${tmpfile_bug}"
rm -fr "${tmpfile_mod}"
fi
flock -u 200
rm -fr "${lock_dir}/patchman.lock"
@@ -170,6 +173,55 @@ check_command_exists() {
fi
}

check_for_modularity() {
modularity=false
if check_command_exists yum ; then
if ${verbose} ; then
echo 'Checking for modularity...'
fi
yum module 2>&1 | grep -q 'No such command'
if [ ${?} == 0 ] ; then
modularity=false
else
modularity=true
fi
fi
}

get_enabled_modules() {
if ${verbose} ; then
echo 'Finding enabled yum modules...'
fi
enabled_modules=$(yum module list --enabled \
| grep "\[e\]" \
| grep -v ^Hint \
| awk {'print $1'})

for module in ${enabled_modules} ; do
module_info=$(yum module info ${module})
module_arch=$(echo ${module_info} | grep Architecture | cut -d : -f 2 | awk '{$1=$1};1')
module_repo=$(echo ${module_info} | grep Repo | cut -d : -f 2 | awk '{$1=$1};1')
module_stream=$(echo ${module_info} | grep Stream | cut -d : -f 2 | sed -e 's/ \[.*//g' | awk '{$1=$1};1')
module_version=$(echo ${module_info} | grep Version | cut -d : -f 2 | awk '{$1=$1};1')
module_context=$(echo ${module_info} | grep Context | cut -d : -f 2 | awk '{$1=$1};1')
module_packages=$(echo $module_info} | sed -n '/Artifacts/,$p' | grep -v Hint | sed -e 's/.* : //g' | grep -v ^$)
OLDIFS=${IFS}
IFS="
"
for package in ${module_packages} ; do
module_package_str="${module_package_str} '${package}'"
done
IFS=${OLDIFS}
echo "'${module}' '${module_stream}' '${module_version}' '${module_context}' '${module_arch}' '${module_repo}' ${module_package_str}" >> ${tmpfile_mod}
done
if [ ! -z ${CPE_NAME} ] ; then
sed -i -e "s#${module_repo}#${CPE_NAME}-${module_repo}#g" "${tmpfile_mod}"
fi
if ${debug} ; then
cat "${tmpfile_mod}"
fi
}

get_installed_rpm_packages() {
if check_command_exists rpm ; then
if ${verbose} ; then
@@ -209,11 +261,16 @@ get_installed_deb_packages() {
fi
done
fi
IFS=${OLDIFS}
}

get_installed_archlinux_packages() {
if check_command_exists pacman ; then
OLDIFS=${IFS}
IFS="
"
pacman -Q -i | awk -v q="'" -v s=" " '/^Name/{n=$3} /^Version/{l=split($3, e, ":"); if (l==2) {ep=e[1]; v=e[2]} else {ep=""; v=$3}; split(v, r, "-")} /^Architecture/{a=$3} /^$/{print q n q s q ep q s q r[1] q s q r[2] q s q a q s q"arch"q}' >> "${tmpfile_pkg}"
IFS=${OLDIFS}
fi
}

@@ -223,6 +280,13 @@ get_packages() {
get_installed_archlinux_packages
}

get_modules() {
check_for_modularity
if ${modularity} ; then
get_enabled_modules
fi
}

get_hostname() {
hostname=$(hostname -f)
if [ -z "${hostname}" ] ; then
@@ -524,8 +588,11 @@ post_data() {
fi
}

if ! check_command_exists which || ! check_command_exists mktemp || ! check_command_exists curl ; then
echo "which, mktemp or curl was not found, exiting."
if ! check_command_exists which || \
! check_command_exists mktemp || \
! check_command_exists curl || \
! check_command_exists flock ; then
echo "which, mktemp, flock or curl was not found, exiting."
exit 1
fi

@@ -553,9 +620,11 @@ tmpfile_pkg=$(mktemp)
tmpfile_rep=$(mktemp)
tmpfile_sec=$(mktemp)
tmpfile_bug=$(mktemp)
tmpfile_mod=$(mktemp)

get_host_data
get_packages
get_modules
if ${repo_check} ; then
get_repos
fi
2 changes: 2 additions & 0 deletions hosts/models.py
Original file line number Diff line number Diff line change
@@ -31,6 +31,7 @@
from repos.models import Repository
from operatingsystems.models import OS
from arch.models import MachineArchitecture
from modules.models import Module
from patchman.signals import info_message, error_message
from packages.utils import get_or_create_package_update
from repos.utils import find_best_repo
@@ -50,6 +51,7 @@ class Host(models.Model):
lastreport = models.DateTimeField()
packages = models.ManyToManyField(Package, blank=True)
repos = models.ManyToManyField(Repository, blank=True, through='HostRepo')
modules = models.ManyToManyField(Module, blank=True)
updates = models.ManyToManyField(PackageUpdate, blank=True)
reboot_required = models.BooleanField(default=False)
host_repos_only = models.BooleanField(default=True)
9 changes: 9 additions & 0 deletions hosts/templates/hosts/host_detail.html
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@
<li class="active"><a data-toggle="tab" href="#host_details">Details</a></li>
<li><a data-toggle="tab" href="#host_updates">Updates Available</a></li>
<li><a data-toggle="tab" href="#host_repos">Repos</a></li>
<li><a data-toggle="tab" href="#host_modules">Modules</a></li>
<li><a data-toggle="tab" href="#host_packages">Packages</a></li>
</ul>

@@ -135,6 +136,14 @@
</div>
</div>

<div class="tab-pane fade in" id="host_modules">
<div class="well well-sm">
<div class="well">
{% gen_table host.modules.all %}
</div>
</div>
</div>

<div class="tab-pane fade in" id="host_packages">
<div class="well well-sm">
<button class="btn btn-primary btn-sm" data-toggle="collapse" data-target="#packages">Show/Hide Installed Packages</button>
Empty file added modules/__init__.py
Empty file.
20 changes: 20 additions & 0 deletions modules/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Copyright 2023 Marcus Furlong <furlongm@gmail.com>
#
# This file is part of Patchman.
#
# Patchman is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3 only.
#
# Patchman is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Patchman. If not, see <http://www.gnu.org/licenses/>

from django.contrib import admin
from modules.models import Module

admin.site.register(Module)
21 changes: 21 additions & 0 deletions modules/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Copyright 2023 Marcus Furlong <furlongm@gmail.com>
#
# This file is part of Patchman.
#
# Patchman is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3 only.
#
# Patchman is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Patchman. If not, see <http://www.gnu.org/licenses/>

from django.apps import AppConfig


class ModulesConfig(AppConfig):
name = 'modules'
22 changes: 22 additions & 0 deletions modules/managers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copyright 2023 Marcus Furlong <furlongm@gmail.com>
#
# This file is part of Patchman.
#
# Patchman is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3 only.
#
# Patchman is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Patchman. If not, see <http://www.gnu.org/licenses/>

from django.db import models


class ModuleManager(models.Manager):
def get_queryset(self):
return super().get_queryset().select_related()
44 changes: 44 additions & 0 deletions modules/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Copyright 2023 Marcus Furlong <furlongm@gmail.com>
#
# This file is part of Patchman.
#
# Patchman is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3 only.
#
# Patchman is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Patchman. If not, see <http://www.gnu.org/licenses/>

from django.db import models
from django.urls import reverse

from arch.models import PackageArchitecture
from packages.models import Package
from repos.models import Repository


class Module(models.Model):

name = models.CharField(unique=True, max_length=255)
stream = models.CharField(unique=True, max_length=255)
version = models.CharField(max_length=255)
context = models.CharField(unique=True, max_length=255)
arch = models.ForeignKey(PackageArchitecture, on_delete=models.CASCADE)
repo = models.ForeignKey(Repository, on_delete=models.CASCADE)
packages = models.ManyToManyField(Package, blank=True)

class Meta:
verbose_name = 'Module'
verbose_name_plural = 'Modules'
ordering = ('name',)

def __str__(self):
return self.name

def get_absolute_url(self):
return reverse('modules:module_detail', args=[str(self.id)])
25 changes: 25 additions & 0 deletions modules/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright 2023 Marcus Furlong <furlongm@gmail.com>
#
# This file is part of Patchman.
#
# Patchman is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3 only.
#
# Patchman is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Patchman. If not, see <http://www.gnu.org/licenses/>

from rest_framework import serializers

from modules.models import Module


class ModuleSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Module
fields = ('id', 'name', 'stream', 'version', 'context', 'arch', 'repo', 'packages')
69 changes: 69 additions & 0 deletions modules/templates/modules/module_detail.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{% extends "base.html" %}

{% load common bootstrap3 %}

{% block page_title %}Module - {{ module }} {% endblock %}

{% block breadcrumbs %} {{ block.super }} <li><a href="{% url 'modules:module_list' %}">Modules</a></li><li class="active">{{ module }}</li>{% endblock %}

{% block content_title %}Module - {{ module }} {% endblock %}

{% block content %}

<ul class="nav nav-tabs">
<li class="active"><a data-toggle="tab" href="#module_details">Details</a></li>
<li><a data-toggle="tab" href="#module_packages">Packages</a></li>
<li><a data-toggle="tab" href="#module_hosts">Hosts with this Module Enabled</a></li>
</ul>

<div class="tab-content">
<div class="tab-pane fade in active" id="module_details">
<div class="well well-sm">
<table class="table table-striped table-bordered table-hover table-condensed table-responsive">
<tr><th>Name</th><td> {{ module.name }} </td></tr>
<tr><th>ID</th><td> {{ module.id }} </td></tr>
<tr><th>Stream</th><td> {{ module.stream }} </td></tr>
<tr><th>Version</th><td> {{ module.version }} </td></tr>
<tr><th>Context</th><td> {{ module.context }} </td></tr>
<tr><th>Architecture</th><td> {{ module.arch }} </td></tr>
<tr><th>Repo</th><td><a href="{{ module.repo.get_absolute_url }}">{{ module.repo }}</a></td></tr>
</table>
</div>
</div>

<div class="tab-pane fade in" id="module_packages">
<div class="well well-sm">
<table class="table table-striped table-bordered table-hover table-condensed table-responsive">
<thead>
<tr>
<th>Name</th>
<th>Epoch</th>
<th>Version</th>
<th>Release</th>
<th>Arch</th>
<th>Type</th>
</tr>
</thead>
<tbody>
{% for package in module.packages.all %}
<tr>
<td> {{ package.name }} </td>
<td> {{ package.epoch }} </td>
<td> {{ package.version }} </td>
<td> {{ package.release }} </td>
<td> {{ package.arch }} </td>
<td> {{ package.get_packagetype_display }} </td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>

<div class="tab-pane fade in" id="module_hosts">
<div class="well well-sm">
{% gen_table module.host_set.all.distinct %}
</div>
</div>
</div>
{% endblock %}
7 changes: 7 additions & 0 deletions modules/templates/modules/module_list.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{% extends "objectlist.html" %}

{% block page_title %}Modules{% endblock %}

{% block breadcrumbs %} {{ block.super }} <li class="active"><a href="{% url 'modules:module_list' %}">Modules</a></li>{% endblock %}

{% block content_title %} Modules {% endblock %}
Loading

0 comments on commit 9c46877

Please sign in to comment.