Skip to content

Commit

Permalink
patch
Browse files Browse the repository at this point in the history
  • Loading branch information
lbr38 committed Dec 17, 2024
1 parent 7a2c890 commit 1ec7c27
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 42 deletions.
11 changes: 11 additions & 0 deletions linupdate.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

# Import libraries
import socket
import signal
from pathlib import Path
from datetime import datetime
from colorama import Fore, Style
Expand All @@ -19,6 +20,7 @@
from src.controllers.Exit import Exit
from src.controllers.ArgsException import ArgsException


#-----------------------------------------------------------------------------------------------
#
# Main function
Expand All @@ -29,6 +31,10 @@ def main():
send_mail = True

try:
# Handle Ctrl+C (KeyboardInterrupt)
signal.signal(signal.SIGINT, signal.default_int_handler)
signal.signal(signal.SIGTERM, signal.default_int_handler)

# Get current date and time
todaydatetime = datetime.now()
date = todaydatetime.strftime('%Y-%m-%d')
Expand Down Expand Up @@ -127,6 +133,11 @@ def main():
except Exception as e:
print('\n' + Fore.RED + ' ✕ ' + Style.RESET_ALL + str(e) + '\n')
exit_code = 1

# If the user presses Ctrl+C or the script is killed, do not send an email and exit with code 2
except (KeyboardInterrupt, SystemExit):
send_mail = False
exit_code = 2

# Exit with exit code and logfile for email report
my_exit.clean_exit(exit_code, send_mail, logsdir + '/' + logfile)
Expand Down
31 changes: 22 additions & 9 deletions src/controllers/App/App.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,17 +64,30 @@ def set_lock(self):

#-----------------------------------------------------------------------------------------------
#
# Remove lock file
# Remove lock files
#
#-----------------------------------------------------------------------------------------------
def remove_lock(self):
if not Path('/tmp/linupdate.lock').is_file():
return

try:
Path('/tmp/linupdate.lock').unlink()
except Exception as e:
raise Exception('Could not remove lock file /tmp/linupdate.lock: ' + str(e))
def remove_locks(self):
# All lock files
locks = [
'/tmp/linupdate.lock',
'/tmp/linupdate.update-running'
]

# Failed locks
failed_locks = []

# Try to remove lock files
for lock in locks:
if Path(lock).is_file():
try:
Path(lock).unlink()
except Exception as e:
failed_locks.append(lock)

# If there are failed locks, raise an exception
if failed_locks:
raise Exception(' Could not remove lock files: ' + ', '.join(failed_locks))


#-----------------------------------------------------------------------------------------------
Expand Down
45 changes: 45 additions & 0 deletions src/controllers/App/Trigger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# coding: utf-8

# Import libraries
from pathlib import Path

class Trigger:
def __init__(self):
self.trigger_path = '/tmp/linupdate.trigger'

#-----------------------------------------------------------------------------------------------
#
# Create trigger file
#
#-----------------------------------------------------------------------------------------------
def create(self, name):
try:
if not Path(self.trigger_path + '.' + name).is_file():
Path(self.trigger_path + '.' + name).touch()
except Exception as e:
raise Exception('Could not create trigger file ' + self.trigger_path + '.' + name + ': ' + str(e))


#-----------------------------------------------------------------------------------------------
#
# Remove trigger file
#
#-----------------------------------------------------------------------------------------------
def remove(self, name):
try:
if Path(self.trigger_path + '.' + name).is_file():
Path(self.trigger_path + '.' + name).unlink()
except Exception as e:
raise Exception('Could not remove trigger file ' + self.trigger_path + '.' + name + ': ' + str(e))


#-----------------------------------------------------------------------------------------------
#
# Return true if trigger file exists
#
#-----------------------------------------------------------------------------------------------
def exists(self, name):
if Path(self.trigger_path + '.' + name).is_file():
return True

return False
18 changes: 10 additions & 8 deletions src/controllers/Exit.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
# coding: utf-8

# Import libraries
from colorama import Fore, Style

# Import classes
import sys
from src.controllers.App.App import App
from src.controllers.Mail import Mail

Expand All @@ -17,6 +15,13 @@ def clean_exit(self, exit_code = 0, send_mail: bool = True, logfile: str = None)
my_app = App()
my_mail = Mail()

# Remove all lock files
try:
my_app.remove_locks()
except Exception as e:
print(str(e))
exit_code = 3

# If send_mail is True, meaning a mail must be sent (if enabled)
# It is not needed to send a mail if the script has just printed the --help for example
if send_mail is True:
Expand All @@ -31,10 +36,7 @@ def clean_exit(self, exit_code = 0, send_mail: bool = True, logfile: str = None)
my_mail.send(subject, 'See report below or attached file.', logfile)
except Exception:
# If mail fails, exit with error code
exit_code = 1

# Remove lock
my_app.remove_lock()
exit_code = 4

# Final exit
exit(exit_code)
sys.exit(exit_code)
58 changes: 41 additions & 17 deletions src/controllers/Module/Reposerver/Agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
import websocket
import json
import sys
import os
from pathlib import Path
from shutil import rmtree
import os

# Import classes
from src.controllers.Log import Log
Expand All @@ -18,6 +18,7 @@
from src.controllers.Module.Reposerver.Config import Config
from src.controllers.Package.Package import Package
from src.controllers.App.Utils import Utils
from src.controllers.App.Trigger import Trigger

class Agent:
def __init__(self):
Expand Down Expand Up @@ -85,23 +86,30 @@ def run_general_checks(self):
#
#-----------------------------------------------------------------------------------------------
def on_inotify_change(self, ev):
# If an update is running by linupdate, then do nothing
# if Path('/tmp/linupdate.update-running').is_file():
# return

# If latest event was less than 120 seconds ago, then do not send again the history
if self.last_inotify_event_time and time.time() - self.last_inotify_event_time < 120:
return
# if self.last_inotify_event_time and time.time() - self.last_inotify_event_time < 120:
# return

# Define new last event time
self.last_inotify_event_time = time.time()
# self.last_inotify_event_time = time.time()

# /var/log/dnf.log issue: wait before sending the history because it might have
# been triggered by another history sending (from a request from the Repomanager server for example) as
# even the 'dnf history' command is logged in the dnf.log file
# So just wait a bit to don't send both history at the same time...
if self.log_file == '/var/log/dnf.log':
time.sleep(15)
# if self.log_file == '/var/log/dnf.log':
# time.sleep(15)

# Send the history
print('[reposerver-agent] New event has been detected in ' + self.log_file + ' - sending history to the Repomanager server.')
self.reposerverStatusController.send_packages_history()
# print('[reposerver-agent] New event has been detected in ' + self.log_file + ' - sending history to the Repomanager server.')
# self.reposerverStatusController.send_packages_history()

print('[reposerver-agent] New event has been detected in ' + self.log_file + ' - triggering packages informations.')
Trigger().create('package-info')


#-----------------------------------------------------------------------------------------------
Expand All @@ -118,19 +126,14 @@ def run_inotify_package_event(self):
self.last_inotify_event_time = None

watch_manager = pyinotify.WatchManager()
# TODO: to test when there are multiple events at once in the log file
# quiet=False => raise Exception
# watch_manager.add_watch(self.log_file, pyinotify.IN_MODIFY, self.on_inotify_change)
watch_manager.add_watch(self.log_file, pyinotify.IN_CLOSE_WRITE, self.on_inotify_change, quiet=False)
notifier = pyinotify.Notifier(watch_manager)
notifier.loop()

# If an exception is raised, then set inotify process as not running and store the exception to
# be read by the main function (cannot raise an exception to be read by the main function, it does not work, so store it instead)
except pyinotify.WatchManagerError as e:
self.inotify_is_running = False
self.inotify_exception = str(e)
except Exception as e:
except (pyinotify.WatchManagerError, Exception) as e:
self.inotify_is_running = False
self.inotify_exception = str(e)

Expand Down Expand Up @@ -390,6 +393,19 @@ def websocket_on_message(self, ws, message):

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

# Case the request is 'update-profile', then retrieve profile configuration from the reposerver
elif message['request'] == 'update-profile':
print('[reposerver-agent] Reposerver requested to update profile configuration')

# Send a response to the reposerver to make the request as running
self.set_request_status(request_id, 'running')

# Log everything to the log file
with Log(log):
self.configController.get_profile_packages_conf()
self.configController.get_profile_repos()

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

Expand Down Expand Up @@ -572,8 +588,7 @@ def websocket_client(self):
#-----------------------------------------------------------------------------------------------
def main(self):
counter = 0
self.child_processes = []
self.child_processes_started = []
self.delayed_processes = []
self.inotify_is_running = False
self.inotify_exception = None
self.websocket_is_running = False
Expand Down Expand Up @@ -622,10 +637,19 @@ def main(self):
thread.start()
except Exception as e:
raise Exception('reposerver websocket connection failed: ' + str(e))

# If some requests logs were not sent (because the program crashed or the reposerver was unavailable for eg), then send them now
self.send_remaining_requests_logs()

# Trigger files
# If package-info has been triggered, then send it
if Trigger().exists('package-info'):
print('[reposerver-agent] Package informations triggered. Sending them to the reposerver')
self.reposerverStatusController.send_packages_info()
time.sleep(5)
Trigger().remove('package-info')

# Sleep 5 seconds before next loop
time.sleep(5)

counter+=1
9 changes: 3 additions & 6 deletions src/controllers/Module/Reposerver/Reposerver.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# Import classes
from src.controllers.Module.Reposerver.Config import Config as Config
from src.controllers.Module.Reposerver.Args import Args
from src.controllers.Module.Reposerver.Status import Status
from src.controllers.App.Trigger import Trigger

class Reposerver:
def __init__(self):
Expand Down Expand Up @@ -49,8 +49,6 @@ def pre(self):
def post(self, updateSummary):
# Note: no need of try / except block here, as it is already handled in the Module pre() function

statusController = Status()

# Quit if there was no packages updates
if updateSummary['update']['status'] == 'nothing-to-do':
print(' ▪ Nothing to do as no packages have been updated')
Expand All @@ -59,9 +57,8 @@ def post(self, updateSummary):
# Generaly "*-release" packages on Redhat/CentOS are resetting .repo files. So it is better to retrieve them again from the reposerver
self.configController.get_profile_repos()

# Send package status to reposerver
# TODO: to test
# statusController.send_packages_info()
# Trigger package-info sending
Trigger().create('package-info')


#-----------------------------------------------------------------------------------------------
Expand Down
9 changes: 8 additions & 1 deletion src/controllers/Package/Package.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ def get_available_packages(self, dist_upgrade: bool = False):
#-----------------------------------------------------------------------------------------------
def update(self, packages_list: list = [], assume_yes: bool = False, ignore_exclusions: bool = False, check_updates: bool = False, dist_upgrade: bool = False, keep_oldconf: bool = True, dry_run: bool = False):
restart_file = '/tmp/linupdate.restart-needed'
update_running_file = '/tmp/linupdate.update-running'

# Package update summary
self.summary = {
Expand Down Expand Up @@ -268,7 +269,10 @@ def update(self, packages_list: list = [], assume_yes: bool = False, ignore_excl
# 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
print('\n ' + Fore.YELLOW + 'Update now [y/N]' + Style.RESET_ALL, end=' ')
if dry_run:
print('\n ' + Fore.YELLOW + 'Update now (dry-run) [y/N]' + Style.RESET_ALL, end=' ')
else:
print('\n ' + Fore.YELLOW + 'Update now [y/N]' + Style.RESET_ALL, end=' ')

answer = input()

Expand All @@ -279,6 +283,9 @@ def update(self, packages_list: list = [], assume_yes: bool = False, ignore_excl
self.remove_all_exclusions()
self.exitController.clean_exit(0, False)

# Create a temporary file in /tmp to indicate that the update process is running
Path(update_running_file).touch()

print('\n Updating packages...')

# If 'linupdate' is in the list of packages to update, then add a temporary file in /tmp to
Expand Down
2 changes: 1 addition & 1 deletion version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.7.0
3.8.0

0 comments on commit 1ec7c27

Please sign in to comment.