Skip to content

Commit

Permalink
3.3.0
Browse files Browse the repository at this point in the history
  • Loading branch information
lbr38 committed Sep 23, 2024
1 parent f4f2e8b commit 6e844d8
Show file tree
Hide file tree
Showing 8 changed files with 169 additions and 11 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/build-and-test-deb.yml
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,9 @@ jobs:
- name: "Run test: check updates"
run: python3 /opt/linupdate/linupdate.py --check-updates

- name: "Run text: update specific packages"
run: python3 /opt/linupdate/linupdate.py --update "curl,wget,apache2"

- name: "Run test: list available modules"
run: python3 /opt/linupdate/linupdate.py --mod-list

Expand Down
3 changes: 2 additions & 1 deletion linupdate.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ def main():
my_module.pre()

# Execute packages update
my_package.update(my_args.assume_yes,
my_package.update(my_args.packages_to_update,
my_args.assume_yes,
my_args.ignore_exclude,
my_args.check_updates,
my_args.dist_upgrade,
Expand Down
22 changes: 22 additions & 0 deletions src/controllers/Args.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def parse(self):
Args.assume_yes = False
Args.check_updates = False
Args.ignore_exclude = False
Args.packages_to_update = []
Args.dist_upgrade = False
Args.keep_oldconf = True

Expand Down Expand Up @@ -72,6 +73,8 @@ def parse(self):
# Set mail recipient
parser.add_argument("--set-mail-recipient", action="store", nargs='?', default="null")

# Packages to update list
parser.add_argument("--update", "-u", action="store", nargs='?', default="null")
# Dist upgrade
parser.add_argument("--dist-upgrade", "-du", action="store_true", default="null")
# Keep oldconf
Expand Down Expand Up @@ -298,8 +301,19 @@ def parse(self):
except Exception as e:
raise ArgsException('Could not set mail recipient(s): ' + str(e))

#
# If --update-list param has been set
#
if args.update != "null":
try:
for package in args.update.split(','):
Args.packages_to_update.append({'name': package.strip()})
except Exception as e:
raise ArgsException('Could not parse update list: ' + str(e))

#
# If --ignore-exclude param has been set
#
if args.ignore_exclude != "null":
Args.ignore_exclude = True

Expand Down Expand Up @@ -629,6 +643,14 @@ def help(self):
{
'title': 'Update options'
},
{
'args': [
'--update',
'-u'
],
'option': 'PACKAGE',
'description': 'Update only the specified packages (separated by commas)'
},
{
'args': [
'--dist-upgrade',
Expand Down
15 changes: 14 additions & 1 deletion src/controllers/Module/Reposerver/Agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,10 +184,23 @@ def websocket_on_message(self, ws, message):

# Log everything to the log file
with Log(log):
self.packageController.update(True)
self.packageController.update([], True)

# Send a summary to the reposerver, with the summary of the update (number of packages updated or failed)
summary = self.packageController.summary

# Case the request is 'request-specific-packages-installation', then update a list of packages
# A list of packages must be provided in the message
elif message['request'] == 'request-specific-packages-installation' and 'packages' in message and len(message['packages']) > 0:
print('[reposerver-agent] Reposerver requested to update a list of packages')

# Log everything to the log file
with Log(log):
self.packageController.update(message['packages'], True)

# Send a summary to the reposerver, with the summary of the installation (number of packages installed or failed)
summary = self.packageController.summary

else:
raise Exception('unknown request sent by reposerver: ' + message['request'])

Expand Down
54 changes: 51 additions & 3 deletions src/controllers/Package/Apt.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,54 @@ def __init__(self):
self.keep_oldconf = True


#-----------------------------------------------------------------------------------------------
#
# Return the current version of a package
#
#-----------------------------------------------------------------------------------------------
def get_current_version(self, package):
try:
# Open apt cache
self.aptcache.open(None)

# Get the package from the cache
pkg = self.aptcache[package]

# If the package is not installed, return an empty string
if not pkg.is_installed:
return ''

# Return the installed version of the package
return pkg.installed.version

except Exception as e:
raise Exception('could not get current version of package ' + package + ': ' + str(e))


#-----------------------------------------------------------------------------------------------
#
# Return the available version of a package
#
#-----------------------------------------------------------------------------------------------
def get_available_version(self, package):
try:
# Open apt cache
self.aptcache.open(None)

# Get the package from the cache
pkg = self.aptcache[package]

# If the package is not installed, return an empty string
if not pkg.is_installed:
return ''

# Return the available version of the package
return pkg.candidate.version

except Exception as e:
raise Exception('could not get available version of package ' + package + ': ' + str(e))


#-----------------------------------------------------------------------------------------------
#
# Return list of installed apt packages, sorted by name
Expand Down Expand Up @@ -252,14 +300,14 @@ def update(self, packagesList, update_method: str = 'one_by_one', exit_on_packag
# If --keep-oldconf is True, then keep the old configuration file
if self.keep_oldconf:
cmd = [
'apt-get', 'install', pkg['name'], '-y',
'apt-get', 'install', pkg['name'] + '=' + pkg['available_version'], '-y',
# Debug only
# '--dry-run',
'--dry-run',
'-o', 'Dpkg::Options::=--force-confdef',
'-o', 'Dpkg::Options::=--force-confold'
]
else:
cmd = ['apt-get', 'install', pkg['name'], '-y']
cmd = ['apt-get', 'install', pkg['name'] + '=' + pkg['available_version'], '-y']

popen = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True)

Expand Down
43 changes: 43 additions & 0 deletions src/controllers/Package/Dnf.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,49 @@ def __init__(self):
}
}

#-----------------------------------------------------------------------------------------------
#
# Return the current version of a package
#
#-----------------------------------------------------------------------------------------------
def get_current_version(self, package):
# Get the current version of the package
# e.g. dnf repoquery --installed --qf="%{version}-%{release}.%{arch}" wget
result = subprocess.run(
["dnf", "repoquery", "--installed", "--qf=%{version}-%{release}.%{arch}", package],
stdout = subprocess.PIPE, # subprocess.PIPE & subprocess.PIPE are alias of 'capture_output = True'
stderr = subprocess.PIPE,
universal_newlines = True # Alias of 'text = True'
)

# Quit if an error occurred
if result.returncode != 0:
raise Exception('could not retrieve current version of package ' + package + ': ' + result.stderr)

return result.stdout.strip()


#-----------------------------------------------------------------------------------------------
#
# Return the available version of a package
#
#-----------------------------------------------------------------------------------------------
def get_available_version(self, package):
# Get the available version of the package
# e.g. dnf repoquery --upgrades --latest-limit 1 --qf="%{version}-%{release}.%{arch}" wget
result = subprocess.run(
["dnf", "repoquery", "--upgrades", "--latest-limit", "1", "--qf=%{version}-%{release}.%{arch}", package],
stdout = subprocess.PIPE, # subprocess.PIPE & subprocess.PIPE are alias of 'capture_output = True'
stderr = subprocess.PIPE,
universal_newlines = True # Alias of 'text = True'
)

# Quit if an error occurred
if result.returncode != 0:
raise Exception('could not retrieve available version of package ' + package + ': ' + result.stderr)

return result.stdout.strip()


#-----------------------------------------------------------------------------------------------
#
Expand Down
38 changes: 33 additions & 5 deletions src/controllers/Package/Package.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,10 @@ def get_available_packages(self, dist_upgrade: bool = False):
#-----------------------------------------------------------------------------------------------
#
# Update packages
# This can be a list of specific packages or all packages
#
#-----------------------------------------------------------------------------------------------
def update(self, assume_yes: bool = False, ignore_exclude: bool = False, check_updates: bool = False, dist_upgrade: bool = False, keep_oldconf: bool = True):
def update(self, packages_list: list = [], assume_yes: bool = False, ignore_exclude: bool = False, check_updates: bool = False, dist_upgrade: bool = False, keep_oldconf: bool = True):
restart_file = '/tmp/linupdate.restart-needed'

# Package update summary
Expand Down Expand Up @@ -170,9 +171,32 @@ def update(self, assume_yes: bool = False, ignore_exclude: bool = False, check_u
# Remove all exclusions before starting (could be some left from a previous run that failed)
self.remove_all_exclusions()

# Retrieve available packages,
# passing the dist_upgrade parameter (which will, with apt, update the list of available packages including packages such as the kernel)
self.packagesToUpdateList = self.get_available_packages(dist_upgrade)
# If a list of packages to update has been provided, use it
if len(packages_list) > 0:
packages_list_temp = []

# For each package in the list, if no current version or available version is provided, retrieve it
# This is the case when the user uses the --update-list parameter
for package in packages_list:
if 'current_version' not in package or 'available_version' not in package:
package['current_version'] = self.myPackageManagerController.get_current_version(package['name'])
package['available_version'] = self.myPackageManagerController.get_available_version(package['name'])

# If current version and available version have been found, then add the package to the final list
if package['current_version'] != '' or package['available_version'] != '':
packages_list_temp.append({
'name': package['name'],
'current_version': package['current_version'],
'available_version': package['available_version']
})

self.packagesToUpdateList = packages_list_temp

# Otherwise, retrieve the list of all available packages
else:
# Retrieve available packages passing the dist_upgrade parameter
# (which will, with apt, update the list of available packages including packages such as the kernel)
self.packagesToUpdateList = self.get_available_packages(dist_upgrade)

# Check for package exclusions
self.exclude(ignore_exclude)
Expand Down Expand Up @@ -203,7 +227,7 @@ def update(self, assume_yes: bool = False, ignore_exclude: bool = False, check_u

# Print the table list of packages to update
# Check prettytable for table with width control https://pypi.org/project/prettytable/
print(tabulate(table, headers=["", "Package", "Current version", "Available version", "Install decision"], tablefmt="simple"), end='\n\n')
print(tabulate(table, headers=["", "Package", "Current version", "Available version", "Update decision"], tablefmt="simple"), end='\n\n')

# If there are no packages to update
if self.packagesToUpdateCount == 0:
Expand All @@ -223,6 +247,10 @@ def update(self, assume_yes: bool = False, ignore_exclude: bool = False, check_u
if self.packagesToUpdateCount == 0:
return

# Print again the number of packages if total count is > 50 to avoid the user to scroll up to see it
if self.packagesToUpdateCount + self.packagesExcludedCount > 50:
print('\n ' + Fore.GREEN + str(self.packagesToUpdateCount) + Style.RESET_ALL + ' packages will be updated, ' + Fore.YELLOW + str(self.packagesExcludedCount) + Style.RESET_ALL + ' will be excluded \n')

# If --assume-yes param has not been specified, then ask for confirmation before installing the printed packages update list
if not assume_yes:
# Ask for confirmation
Expand Down
2 changes: 1 addition & 1 deletion version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.2.0
3.3.0

0 comments on commit 6e844d8

Please sign in to comment.