Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3.8.0 #80

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 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,9 @@ def main():
send_mail = True

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

# Get current date and time
todaydatetime = datetime.now()
date = todaydatetime.strftime('%Y-%m-%d')
Expand Down Expand Up @@ -127,6 +132,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 as e:
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
12 changes: 3 additions & 9 deletions src/controllers/Args.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,7 @@ def parse(self):

# Catch exceptions
# Either ArgsException or Exception, it will always raise an ArgsException to the main script, this to avoid sending an email when an argument error occurs
except ArgsException as e:
raise ArgsException(str(e))
except Exception as e:
except (ArgsException, Exception) as e:
raise ArgsException(str(e))

try:
Expand Down Expand Up @@ -595,9 +593,7 @@ def parse(self):

# Catch exceptions
# Either ArgsException or Exception, it will always raise an ArgsException to the main script, this to avoid sending an email when an argument error occurs
except ArgsException as e:
raise ArgsException(str(e))
except Exception as e:
except (ArgsException, Exception) as e:
raise ArgsException(str(e))


Expand Down Expand Up @@ -849,7 +845,5 @@ def help(self):

# Catch exceptions
# Either ArgsException or Exception, it will always raise an ArgsException to the main script, this to avoid sending an email when an argument error occurs
except ArgsException as e:
raise ArgsException('Printing help error: ' + str(e))
except Exception as e:
except (ArgsException, Exception) as e:
raise ArgsException('Printing help error: ' + str(e))
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)
57 changes: 32 additions & 25 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,13 @@ def run_general_checks(self):
#
#-----------------------------------------------------------------------------------------------
def on_inotify_change(self, ev):
# 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:
# If an update is running by linupdate, then do nothing for now
if Path('/tmp/linupdate.update-running').is_file():
return

# Define new last event 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)

# 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()
# Message for debugging
# print('[reposerver-agent][debug] New event has been detected in ' + self.log_file + ' - triggering packages informations.')
Trigger().create('package-info')


#-----------------------------------------------------------------------------------------------
Expand All @@ -118,19 +109,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 +376,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 +571,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 +620,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
Loading
Loading