From 163fd5ca9d72348f16116019456b5557c1de334d Mon Sep 17 00:00:00 2001 From: Jean-Philippe Pialasse Date: Thu, 4 Apr 2024 17:20:45 -0400 Subject: [PATCH] * Wed Mar 27 2024 Jean-Philippe Pialasse 11.0.0-5.sme - rewrite smeserver.py plugin [SME: 12113] - fix migrate template 39VisibleSmeContribs, 45RockyEOL [SME: 12549] --- .../migrate/39VisibleSmeContribs | 2 +- .../site-packages/dnf-plugins/smeserver.py | 518 ++++++++++++++++++ root/usr/lib/yum-plugins/smeserver.py | 447 --------------- smeserver-update.spec | 10 +- 4 files changed, 525 insertions(+), 452 deletions(-) create mode 100644 root/usr/lib/python3.6/site-packages/dnf-plugins/smeserver.py delete mode 100644 root/usr/lib/yum-plugins/smeserver.py diff --git a/root/etc/e-smith/db/yum_repositories/migrate/39VisibleSmeContribs b/root/etc/e-smith/db/yum_repositories/migrate/39VisibleSmeContribs index deeee9c..47a6328 100644 --- a/root/etc/e-smith/db/yum_repositories/migrate/39VisibleSmeContribs +++ b/root/etc/e-smith/db/yum_repositories/migrate/39VisibleSmeContribs @@ -4,7 +4,7 @@ my $repo = $DB->get($name) or next; #only migrate if restore from previous SME version, we do not want to override an admin setting for current version. return unless $repo->prop('MirrorList') =~ m{/$name-1?[0789]{1}$}; - $repo->set_prop('status', 'enabled') + $repo->set_prop('status', 'enabled'); $repo->set_prop('Visible', 'yes'); } } diff --git a/root/usr/lib/python3.6/site-packages/dnf-plugins/smeserver.py b/root/usr/lib/python3.6/site-packages/dnf-plugins/smeserver.py new file mode 100644 index 0000000..4afe841 --- /dev/null +++ b/root/usr/lib/python3.6/site-packages/dnf-plugins/smeserver.py @@ -0,0 +1,518 @@ +# vim: noexpandtab tabstop=4 + +import os +import shutil +import subprocess +import syslog + +from dnfpluginscore import _, logger +import libdnf.conf +import dnf +import dnf.transaction + + +events_path = '/etc/e-smith/events' +initialize_database = events_path + '/actions/initialize-default-databases' +navigation_conf = events_path + '/actions/navigation-conf' +systemctl = "/usr/bin/systemctl" +# not used +#dnf_update_dbs = events_path + '/actions/yum-update-dbs' +#Conflicts=yum-cron.service dnf-automatic-notifyonly.service dnf-automatic.service +signal_event = '/sbin/e-smith/signal-event' +config_set = '/sbin/e-smith/config' +status_file = '/var/cache/dnf/dnf.status' +#not used +#expand_template = '/sbin/e-smith/expand-template' +#not used +#service = '/sbin/e-smith/service' + +eventlist = dict() +actionlist = dict() +templateslist = dict() +serviceslist = dict() + + +# list of packages that need a reboot +# if a package name starts with one these names, a reboot is needed +rebootpkgs = ['daemontools', 'dbus', 'glibc', 'gnutls', 'kernel', 'linux-firmware', 'lvm2', 'mdadm', 'openssl-libs', 'systemd'] +# exclusions : +rebootpkgsexclude = dict() +rebootpkgsexclude['dbus']='-python','-tests','-devel','-doc', +rebootpkgsexclude['glibc']='-devel','-headers','-statics','-utils','-glib', +rebootpkgsexclude['gnutls']='-devel', +rebootpkgsexclude['kernel']='-doc','-debug','-devel','-abi-whitelists','tools', +rebootpkgsexclude['lvm2']='-devel','-python', +rebootpkgsexclude['systemd']='-devel','-python', + + +# list of packages that need a service restart +# if a package name starts with one of these values, a service restart is needed +# since, for example, httpd means httpd-admin and httpd-e-smith, we won't use these names directly but via serviceslist (see below) +restartpkgs = ['dovecot','bglibs','cvm','freeradius','httpd','iptables', 'mariadb' , 'nut', 'openldap', 'openssh', 'php', 'pptpd', 'proftp', 'samba', 'spamassassin', 'squid', 'qmail', 'qpsmtpd'] +servicenames = dict() +servicenames['bglibs']='cvm-unix', +servicenames['cvm']='cvm-unix', +servicenames['dovecot']='dovecot', +servicenames['freeradius']='radiusd', +servicenames['httpd']='httpd-admin','httpd-e-smith', +servicenames['iptables']='masq', +servicenames['mariadb']='mariadb', +servicenames['nut']='nut-server','nut-driver','nut-monitor', +servicenames['openldap']='ldap', +servicenames['openssh']='sshd', +servicenames['php']='httpd-e-smith', 'php72-php-fpm', 'php73-php-fpm', 'php74-php-fpm', 'php80-php-fpm', 'php81-php-fpm', 'php82-php-fpm', 'php83-php-fpm', +servicenames['proftp']='ftp', +servicenames['samba']='smb', +servicenames['spamassassin']='spamassassin', +servicenames['squid']='squid', +servicenames['qmail']='qmail', +servicenames['qpsmtpd']='qpsmtpd', 'sqpsmtpd', + +DEBUG = True +smechange = False +smechangelist = dict() +ourfile = False +# not used +erasing = False +# not used +smeEventPretrans = False +removenorebootok = dict() + +class SMEServer(dnf.Plugin): + + name = "smeserver" + + def __init__(self, base, cli): + super(SMEServer, self).__init__(base, cli) + self.base = base + self.logger = logger + self.report_yum_status('init') + self.log("smeserver.py: __init__") + + def log(self,s): + if DEBUG : + print(s) + + def createevent(self): + if os.path.isdir('/etc/e-smith/events/temp'): + shutil.rmtree('/etc/e-smith/events/temp') + os.makedirs('/etc/e-smith/events/temp/services2adjust', 0o777) + os.makedirs('/etc/e-smith/events/temp/templates2expand', 0o777) + + def addtemplate2expand(self,template): + #check filename + head_tail = os.path.split(template) + head = head_tail[0]; + filename = head_tail[1]; + # create dir + if not os.path.isdir('/etc/e-smith/events/temp/templates2expand' + head): + os.makedirs('/etc/e-smith/events/temp/templates2expand' + head, 0o777) + #touch file + fname= '/etc/e-smith/events/temp/templates2expand'+ template + fhandle = open(fname, 'a') + try: + os.utime(fname, None) + finally: + fhandle.close() + + def report_yum_status(self,status): + fileHandle = open(status_file, 'w') + fileHandle.write(status) + fileHandle.close() + + def pre_config(self): + self.report_yum_status('pre_config') + self.log("smeserver.py: pre_config") + + def config(self): + self.report_yum_status('config') + self.log("smeserver.py: config") + + def sack(self): + global ourfile + ourfile = True + self.report_yum_status('sack') + self.log("smeserver.py: sack") + + def resolved(self): + self.report_yum_status('resolved') + self.log("smeserver.py: resolved") +#2 PKG_DOWNGRADE = dnf.transaction.PKG_DOWNGRADE # :api +#1 PKG_INSTALL = dnf.transaction.PKG_INSTALL # :api +#4 PKG_OBSOLETE = dnf.transaction.PKG_OBSOLETE # :api +#9 PKG_REINSTALL = dnf.transaction.PKG_REINSTALL # :api +#8 PKG_REMOVE = dnf.transaction.PKG_ERASE # :api +#6 PKG_UPGRADE = dnf.transaction.PKG_UPGRADE # :api +#7 PKG_UPGRADED = libdnf.transaction.TransactionItemAction_UPGRADED +#101 PKG_CLEANUP = dnf.transaction.PKG_CLEANUP # :api +#102 PKG_VERIFY = dnf.transaction.PKG_VERIFY # :api +#103 PKG_SCRIPTLET = dnf.transaction.PKG_SCRIPTLET # :api + + def pre_transaction(self): + self.report_yum_status('pretrans') + self.log("smeserver.py: Pretrans") + # Prefetch filelist for packages to be removed, + in_ts_items = [] + out_ts_items = [] + all_ts_items = [] + for ts_item in self.base.transaction: + if ts_item.action in dnf.transaction.FORWARD_ACTIONS: + in_ts_items.append(ts_item) + elif ts_item.action in dnf.transaction.BACKWARD_ACTIONS: + out_ts_items.append(ts_item) + else: + # The action is not rpm change. It can be a reason change, therefore we can skip that item + continue + all_ts_items.append(ts_item) + + allpkgs = all_ts_items # [tsi.pkg for tsi in all_ts_items] + # also to see dnf.db.group.RPMTransaction.install_set + installs = in_ts_items #[tsi.pkg for tsi in in_ts_items] + installs_dict = {} + for item in installs: + nam = item.name + installs_dict[nam] = item + # one liner new_dict = {item['name']:item for item in data} + # also to see dnf.db.group.RPMTransaction.remove_set + removes = out_ts_items #[tsi.pkg for tsi in out_ts_items] + self.log('## allpkgs: ' + str([ str(tsi.pkg) for tsi in allpkgs])) + self.log('## installs: ' +str([ str(tsi.pkg) for tsi in installs])) + self.log('## Removes: ' + str([ str(tsi.pkg) for tsi in removes])) + ##allpkgs: ['smeserver-tinydns-11.0.0-4.el8.sme.noarch', 'smeserver-tinydns-11.0.0-3.el8.sme.noarch'] + ## install: ['smeserver-tinydns-11.0.0-4.el8.sme.noarch'] + ## Remove: ['smeserver-tinydns-11.0.0-3.el8.sme.noarch'] + + # list of all instaled rpm + base = dnf.Base() + base.fill_sack() + q = base.sack.query() + rpmdb = q.installed() + + global smechange + global eventlist + global actionlist + global templateslist + global serviceslist + global erasing + global smeEventPretrans + global removenorebootok + + + # reinstall are not listed there so already skipped + # downgrade are going there + # update are going there, we filter them out + # real removal are the real deal there ; downgrade are played like if they were removal + for tsmem in removes: + erasing = True + n= str(tsmem.name) + # if the pkg.name is present on both IN and OUT, then we assume update/downgrade and continue to transaction + if n in installs_dict : + self.log('updating ==> state ' + str(tsmem.pkg) + " to " + str(installs_dict[n].pkg) ) + continue + self.log('**Package: ' + str(tsmem.pkg) + ' to be removed') + smeevent = n + '-update' + #check if /etc/e-smith/events/$smeevent exist, if yes add it to todo list + if os.path.isdir(events_path + os.sep + smeevent): + tmppath = events_path + os.sep + smeevent + os.sep + eventlist[n] = smeevent + + # if templates to expand, add them + if os.path.isdir(tmppath + "templates2expand" + os.sep): + tmppathtmpl = tmppath + "templates2expand" + os.sep + for subdir, dirs, files in os.walk(tmppathtmpl): + for file in files: + mytmptemplate = os.path.join(subdir, file).replace(tmppathtmpl,os.sep) + if mytmptemplate not in templateslist : + templateslist[mytmptemplate]=mytmptemplate + self.log(" template " + mytmptemplate) + + # we add the actions scripts + tmppathsrv = tmppath + os.sep + files = [f for f in os.listdir(tmppathsrv)] + for f in files: + if os.path.islink(tmppathsrv + f): + mytmpaction = os.path.realpath(os.readlink(tmppath + f).replace("..",events_path)) + self.log(" link " + f + ": " + mytmpaction) + actionlist[f]=mytmpaction + + # if there are services to handle we had them + if os.path.isdir(tmppath + "services2adjust" + os.sep): + tmppathsrv = tmppath + "services2adjust" + os.sep + files = [f for f in os.listdir(tmppathsrv)] + for f in files: + if os.path.islink(tmppathsrv + f): + self.log(" service link " + f + ": " + (os.readlink(tmppathsrv + f))) + mytmpserv = os.readlink(tmppathsrv + f) + # we had the service with its action, except if we need to force a restart + # TODO as we have only pkgs to remove here, shouldn't we just stop the service NOW? + if f not in serviceslist : + serviceslist[f]=mytmpserv + else: + if mytmpserv == "restart": + self.log("overriding all signals, forcing restart " + f ) + serviceslist[f]="restart" + removenorebootok[n]=1; + else: + # if we are here this is a smeserver pkg without an update event, needs reboot... + if (n.startswith('smeserver') or n.startswith('e-smith')) and not n.startswith('smeserver-locale'): + smechange = True + smechangelist[n]=str(tsmem.installed) + " " + str(tsmem.pkg) + self.log("smechange set to True because of " + n + " without an update event " + str(tsmem.installed) + " " + str(tsmem.pkg) ) + #end of if smeserver-*-update event exist + # do we need this for removal ? + if "/etc/e-smith/web/panels/manager/cgi-bin" in tsmem.files : + self.log(" ==> pannel detected : adding navigation-conf") + actionlist['S80navigation-conf']="/etc/e-smith/events/actions/navigation-conf" + + #end of for list of packages + #only for debug + self.log('## smechange: '+str(smechange)) + self.log('## eventlist: '+ str(eventlist)) + self.log('## templateslist: '+str(templateslist.keys())) + self.log('## serviceslist: '+str(serviceslist.items())) + self.log('## actions ALL: ' + str(actionlist.keys())) + self.log('## smechangelist: ' + str(smechangelist.keys())) + + if len(serviceslist)>0: + # would it be a good idea to do some action/event there to stop service being removed ? + # for the moment we do not, logic : if template still there at posttrans, we expand it after removal + # probably the actions in some situations could be done before... + # create an empty temp event + print("Creating temporary event 'temp' and populating it...") + self.createevent() + print(" Adding services to adjust") + for kservices in serviceslist: + self.log(" " + kservices + ": " + serviceslist[kservices]) + os.symlink(serviceslist[kservices], '/etc/e-smith/events/temp/services2adjust/' + kservices) + + # execute the event ; should we really wait ?? + print("Executing signal-event temp before uninstalling ...........") + os.spawnl(os.P_WAIT,signal_event,signal_event, 'temp') + smeEventPretrans = True; + + def transaction(self): + self.report_yum_status('transaction') + self.log("smeserver.py: transaction") + + #ts = self.getTsInfo() + #rpmdb = self.getRpmDB() + # Prefetch filelist for packages to be removed, + in_ts_items = [] + out_ts_items = [] + all_ts_items = [] + for ts_item in self.base.transaction: + if ts_item.action in dnf.transaction.FORWARD_ACTIONS: + in_ts_items.append(ts_item) + elif ts_item.action in dnf.transaction.BACKWARD_ACTIONS: + out_ts_items.append(ts_item) + else: + # The action is not rpm change. It can be a reason change, therefore we can skip that item + continue + all_ts_items.append(ts_item) + + allpkgs = all_ts_items # [tsi.pkg for tsi in all_ts_items] + # also to see dnf.db.group.RPMTransaction.install_set + installs = in_ts_items #[tsi.pkg for tsi in in_ts_items] + installs_dict = {} + for item in installs: + nam = item.name + installs_dict[nam] = item + # one liner new_dict = {item['name']:item for item in data} + # also to see dnf.db.group.RPMTransaction.remove_set + removes = out_ts_items #[tsi.pkg for tsi in out_ts_items] + + global smechange + global eventlist + global actionlist + global templateslist + global serviceslist + global erasing + global smeEventPretrans + global removenorebootok + global ourfile + + for tsmem in installs: + # n: name ; a : arch; e: epoch ; v: version, r: release + n = tsmem.name + a = tsmem.arch + e = tsmem.epoch + v = tsmem.version + r = tsmem.release + + # if we're upgrading/installing/removing a rebootpkgs package.. a reboot is required + for pkg in rebootpkgs: + if n.startswith(pkg): + self.log('**Package: ' + n + ' triggers reboot') + # we do some exclusions -devel -doc ... + if pkg in rebootpkgsexclude: + cont=False + for name in rebootpkgsexclude[pkg]: + if n.startswith(pkg + name): + cont=True + if cont: + # this is an exception we do not need reboot : -devel, -doc ... + continue + # either no exception or does not fit exceptions: we need reboot + smechange = True + smechangelist[n]=str(n) + "-" + str(v) + "-" + str(r) + self.log("smechange set to True because of " + n + " with " + str(v) + "-" + str(r) + " " + str(tsmem.installed)) + #self.log("smechange set to True because of " + pkg) + + # check if we're upgrading a restartpkgs rpm + for pkg in restartpkgs: + if n.startswith(pkg): + if pkg in servicenames: + for name in servicenames[pkg]: + if name not in serviceslist : + serviceslist[name] = 'restart' + else: + if pkg not in serviceslist : + serviceslist[pkg] = 'restart' + + + #if smechange is true we can ignore the following part + # wondering if we reallly do want to ignore that : let's say we want to delay kernel reboot, + # could we at least configure httpd cleanly until next week ? + # also when updating a package the erasing flag was on and prevented to do this .... + # if not smechange and not erasing: + #if True: + + smeevent = n + '-update' + if os.path.isdir(events_path + os.sep + smeevent): + tmppath = events_path + os.sep + smeevent + os.sep + eventlist[n] = smeevent + + files = [f for f in os.listdir(tmppath)] + for f in files: + if os.path.islink(tmppath + f): + + mytmpaction = os.path.realpath(os.readlink(tmppath + f).replace("..",events_path)) + #self.log(" link " + f + ": " + mytmpaction) + actionlist[f]=mytmpaction + + + if os.path.isdir(tmppath + "templates2expand" + os.sep): + tmppathtmpl = tmppath + "templates2expand" + os.sep + for subdir, dirs, files in os.walk(tmppathtmpl): + for file in files: + mytmptemplate = os.path.join(subdir, file).replace(tmppathtmpl,os.sep) + if mytmptemplate not in templateslist : + templateslist[mytmptemplate]=mytmptemplate + self.log(" template " + mytmptemplate) + + if os.path.isdir(tmppath + "services2adjust" + os.sep): + tmppathsrv = tmppath + "services2adjust" + os.sep + files = [f for f in os.listdir(tmppathsrv)] + for f in files: + if os.path.islink(tmppathsrv + f): + self.log(" service link " + f + ": " + (os.readlink(tmppathsrv + f))) + mytmpserv = os.readlink(tmppathsrv + f) + if f not in serviceslist : + serviceslist[f]=mytmpserv + else: + if mytmpserv == "restart": + #only for debug + self.log("overriding all signals, forcing restart " + f) + serviceslist[f]="restart" + else: + if (n.startswith('smeserver') or n.startswith('e-smith')) and not n.startswith('smeserver-locale') and not (n in removenorebootok): + smechange = True + smechangelist[n]=str(tsmem.po.state) + " " + str(tsmem.current_state) + " " + str(tsmem.output_state) + self.log("smechange set to True because of " + n + " with " + str(tsmem.po.state) + " " + str(tsmem.current_state) + " " + str(tsmem.output_state) ) + + # as long as it is a sme pkg we need to rebuild panel + if "/etc/e-smith/web/panels/manager/cgi-bin" in tsmem.files : + self.log(" ==> pannel detected : adding navigation-conf") + actionlist['S80navigation-conf']="/etc/e-smith/events/actions/navigation-conf" + # end of for installed list + + #only for debug + self.log('## smechange: '+str(smechange)) + self.log('## eventlist: '+ str(eventlist)) + self.log('## templateslist: '+str(templateslist.keys())) + self.log('## serviceslist: '+str(serviceslist.items())) + self.log('## actions ALL: ' + str(actionlist.keys())) + self.log('## smechangelist: ' + str(smechangelist.keys())) + + # check if smechange is true or if eventlist has items.. in both cases we must initialize databases + if smechange == True or len(eventlist)>0: + print('Initializing databases') + os.spawnl(os.P_WAIT, initialize_database, initialize_database) + + # just in case we add systemd-default and systemd-reload + if 'S88systemd-default' not in actionlist : + actionlist['S88systemd-default'] = '/etc/e-smith/events/actions/systemd-default' + if 'S89systemd-reload' not in actionlist : + actionlist['S89systemd-reload'] = '/etc/e-smith/events/actions/systemd-reload' + + # here we would like to remove duplicates + # those are potential : S??navigation-conf S??systemd-reload S??systemd-default + for act in ('navigation-conf','systemd-reload','systemd-default'): + global iter + iter = 0 + for key in actionlist.keys(): + if key.endswith(act): + iter += 1 + if iter>1: + del actionlist[key] + self.log("removing duplicate action " + key + " iter " + str(iter)) + + self.report_yum_status('transaction') + self.log("smeserver.py: transaction") + + # now, if smechange is false (no reboot needed), we can execute actions, expand templates and adjust services + + # create an empty temp event + print("Creating temporary event 'temp' and populating it...") + self.createevent() + + # fill it with our list of actions + if len(actionlist)>0: + print(" Adding actions to execute") + for kactions in actionlist: + if os.path.isfile(actionlist[kactions]): + self.log(" " + kactions) + os.symlink(actionlist[kactions],'/etc/e-smith/events/temp/' + kactions) + + # fill it with our list of templates to expand + if len(templateslist)>0: + print(" Adding templates to expand") + for ktemplates in templateslist: + mytemplatedir = '/etc/e-smith/templates/' + ktemplates + mytemplatemeta = '/etc/e-smith/templates.metadata/' + ktemplates + if os.path.exists(mytemplatedir) or os.path.exists(mytemplatemeta): + self.log(" " + ktemplates) + self.addtemplate2expand(ktemplates) + + if len(serviceslist)>0: + print(" Adding services to adjust") + for kservices in serviceslist: + self.log(" " + kservices + ": " + serviceslist[kservices]) + os.symlink(serviceslist[kservices], '/etc/e-smith/events/temp/services2adjust/' + kservices) + + # execute the event ; should we really wait ?? + print("Executing signal-event temp ...........") + os.spawnl(os.P_WAIT,signal_event,signal_event, 'temp') + + if smechange: + os.spawnl(os.P_WAIT, config_set, config_set, 'set', 'UnsavedChanges', 'yes') + os.spawnl(os.P_WAIT, navigation_conf, navigation_conf) + syslog.syslog('Needs signal-event post-upgrade; signal-event reboot because of: ' + str(smechangelist.keys()) ) + print("Reload dnf db for server-manager") + os.spawnl(os.P_WAIT, systemctl, systemctl, 'restart', 'dnf') + + #no more close hook + #def close_hook(conduit): + if ourfile and os.path.isfile(status_file): + os.unlink(status_file) + + if smechange: + print("\nThe following updates require a server reboot:\n" + str(smechangelist.keys())) + print("\n==============================================================\n") + print("WARNING: You now need to run BOTH of the following commands") + print("to ensure consistent system state:") + print("signal-event post-upgrade; signal-event reboot") + print("You should run these commands unless you are certain that") + print("dnf made no changes to your system.\n") + print("==============================================================") + diff --git a/root/usr/lib/yum-plugins/smeserver.py b/root/usr/lib/yum-plugins/smeserver.py deleted file mode 100644 index 5c4e581..0000000 --- a/root/usr/lib/yum-plugins/smeserver.py +++ /dev/null @@ -1,447 +0,0 @@ -# vim: noexpandtab tabstop=4 - -import os -import shutil -import subprocess -import syslog -from yum.constants import * -from yum.plugins import PluginYumExit -from yum.plugins import TYPE_CORE -from yum.packages import parsePackages -from yum.packages import RpmBase - -requires_api_version = '2.1' -plugin_type = (TYPE_CORE,) - -events_path = '/etc/e-smith/events' -initialize_database = events_path + '/actions/initialize-default-databases' -navigation_conf = events_path + '/actions/navigation-conf' -systemctl = "/usr/bin/systemctl" -dnf_update_dbs = events_path + '/actions/yum-update-dbs' -Conflicts=yum-cron.service dnf-automatic-notifyonly.service dnf-automatic.service -signal_event = '/sbin/e-smith/signal-event' -config_set = '/sbin/e-smith/config' -status_file = '/var/cache/dnf/dnf.status' -expand_template = '/sbin/e-smith/expand-template' -service = '/sbin/e-smith/service' - -eventlist = dict() -actionlist = dict() -templateslist = dict() -serviceslist = dict() - - -# list of packages that need a reboot -# if a package name starts with one these names, a reboot is needed -rebootpkgs = ['daemontools', 'dbus', 'glibc', 'gnutls', 'kernel', 'linux-firmware', 'lvm2', 'mdadm', 'openssl-libs', 'systemd'] -# exclusions : -rebootpkgsexclude = dict() -rebootpkgsexclude['dbus']='-python','-tests','-devel','-doc', -rebootpkgsexclude['glibc']='-devel','-headers','-statics','-utils','-glib', -rebootpkgsexclude['gnutls']='-devel', -rebootpkgsexclude['kernel']='-doc','-debug','-devel','-abi-whitelists','tools', -rebootpkgsexclude['lvm2']='-devel','-python', -rebootpkgsexclude['systemd']='-devel','-python', - - -# list of packages that need a service restart -# if a package name starts with one of these values, a service restart is needed -# since, for example, httpd means httpd-admin and httpd-e-smith, we won't use these names directly but via serviceslist (see below) -restartpkgs = ['dovecot','bglibs','cvm','freeradius','httpd','iptables', 'mariadb' , 'nut', 'openldap', 'openssh', 'php', 'pptpd', 'proftp', 'samba', 'spamassassin', 'squid', 'qmail', 'qpsmtpd'] -servicenames = dict() -servicenames['bglibs']='cvm-unix', -servicenames['cvm']='cvm-unix', -servicenames['dovecot']='dovecot', -servicenames['freeradius']='radiusd', -servicenames['httpd']='httpd-admin','httpd-e-smith', -servicenames['iptables']='masq', -servicenames['mariadb']='mariadb', -servicenames['nut']='nut-server','nut-driver','nut-monitor', -servicenames['openldap']='ldap', -servicenames['openssh']='sshd', -servicenames['php']='httpd-e-smith', 'php-fpm', 'php55-php-fpm', 'php56-php-fpm', 'php70-php-fpm', 'php71-php-fpm', 'php72-php-fpm', 'php73-php-fpm', 'php74-php-fpm', 'php80-php-fpm', 'php81-php-fpm', -servicenames['proftp']='ftp', -servicenames['samba']='smb', -servicenames['spamassassin']='spamassassin', -servicenames['squid']='squid', -servicenames['qmail']='qmail', -servicenames['qpsmtpd']='qpsmtpd', - -smechange = False -smechangelist = dict() -ourfile = False -erasing = False -DEBUG = False -smeEventPretrans = False -removenorebootok = dict() - -def log(s): - if DEBUG : - print s -def createevent(): - if os.path.isdir('/etc/e-smith/events/temp'): - shutil.rmtree('/etc/e-smith/events/temp') - os.makedirs('/etc/e-smith/events/temp/services2adjust', 0o777) - os.makedirs('/etc/e-smith/events/temp/templates2expand', 0o777) - -def addtemplate2expand(template): - #check filename - head_tail = os.path.split(template) - head = head_tail[0]; - filename = head_tail[1]; - # create dir - if not os.path.isdir('/etc/e-smith/events/temp/templates2expand' + head): - os.makedirs('/etc/e-smith/events/temp/templates2expand' + head, 0o777) - #touch file - fname= '/etc/e-smith/events/temp/templates2expand'+ template - fhandle = open(fname, 'a') - try: - os.utime(fname, None) - finally: - fhandle.close() - -def report_yum_status(status): - fileHandle = open(status_file, 'w') - fileHandle.write(status) - fileHandle.close() - -def predownload_hook(conduit): - report_yum_status('predownload') - -def postdownload_hook(conduit): - report_yum_status('postdownload') - -def prereposetup_hook(conduit): - global ourfile - - ourfile = True - report_yum_status('prereposetup') - -def postreposetup_hook(conduit): - report_yum_status('postreposetup') - -def exclude_hook(conduit): - report_yum_status('exclude') - -def preresolve_hook(conduit): - report_yum_status('preresolve') - -def postresolve_hook(conduit): - report_yum_status('postresolve') - -def pretrans_hook(conduit): -# we might need to change the strategy here to make a difference between update and removal. -#transaction set states -#TS_UPDATE = 10 -#TS_INSTALL = 20 -#TS_TRUEINSTALL = 30 -#TS_ERASE = 40 -#TS_OBSOLETED = 50 -#TS_OBSOLETING = 60 -#TS_AVAILABLE = 70 -#TS_UPDATED = 90 -#TS_FAILED = 100 -#TS_INSTALL_STATES = [TS_INSTALL, TS_TRUEINSTALL, TS_UPDATE, TS_OBSOLETING] -#TS_REMOVE_STATES = [TS_ERASE, TS_OBSOLETED, TS_UPDATED] - - report_yum_status('pretrans') - log("*******Pretrans********") - # Prefetch filelist for packages to be removed, - # otherwise for updated packages headers will not be available - ts = conduit.getTsInfo() - removes = ts.getMembersWithState(output_states=TS_REMOVE_STATES) - rpmdb = conduit.getRpmDB() - global smechange - global eventlist - global actionlist - global templateslist - global serviceslist - global erasing - global smeEventPretrans - global removenorebootok - - - # reinstall are not listed there so already skipped - # downgrade are going there - # update are going there, we filter them out - # real removal are the real deal there ; downgrade are played like if they were removal - for tsmem in removes: - erasing = True - (n, a, e, v, r) = tsmem.po.pkgtup - if tsmem.output_state == TS_UPDATED : - # if we are actually updating then we are not removing, and new package will do better... skipping - log('skiping as updating ==> state ' + str(tsmem.po.state) + " RPM current state" + str(tsmem.current_state) + " RPM output state" + str(tsmem.output_state)) - continue - log('**Package: ' + tsmem.name + ' to be removed') - smeevent = tsmem.name + '-update' - if os.path.isdir(events_path + os.sep + smeevent): - tmppath = events_path + os.sep + smeevent + os.sep - eventlist[tsmem.name] = smeevent - - if os.path.isdir(tmppath + "templates2expand" + os.sep): - tmppathtmpl = tmppath + "templates2expand" + os.sep - for subdir, dirs, files in os.walk(tmppathtmpl): - for file in files: - mytmptemplate = os.path.join(subdir, file).replace(tmppathtmpl,os.sep) - if not templateslist.has_key(mytmptemplate): - templateslist[mytmptemplate]=mytmptemplate - log(" template " + mytmptemplate) - - # nothing for actions ??? - tmppathsrv = tmppath + os.sep - files = [f for f in os.listdir(tmppathsrv)] - for f in files: - if os.path.islink(tmppathsrv + f): - mytmpaction = os.path.realpath(os.readlink(tmppath + f).replace("..",events_path)) - log(" link " + f + ": " + mytmpaction) - actionlist[f]=mytmpaction - - # end debug print - if os.path.isdir(tmppath + "services2adjust" + os.sep): - tmppathsrv = tmppath + "services2adjust" + os.sep - files = [f for f in os.listdir(tmppathsrv)] - for f in files: - if os.path.islink(tmppathsrv + f): - log(" service link " + f + ": " + (os.readlink(tmppathsrv + f))) - mytmpserv = os.readlink(tmppathsrv + f) - if not serviceslist.has_key(f): - serviceslist[f]=mytmpserv - else: - if mytmpserv == "restart": - log("overriding all signals, forcing restart " + f ) - serviceslist[f]="restart" - removenorebootok[n]=1; - thispo = rpmdb.searchNevra(name=n, arch=a, epoch=e, ver=v, rel=r)[0] - # thispo.dirlist thispo.ghostlist thispo.filelist - if "/etc/e-smith/web/panels/manager/cgi-bin" in thispo.dirlist: - log(" ==> pannel detected : adding navigation-conf") - actionlist['S80navigation-conf']="/etc/e-smith/events/actions/navigation-conf" - - else: - if (n.startswith('smeserver') or n.startswith('e-smith')) and not n.startswith('smeserver-locale'): - smechange = True - smechangelist[n]=str(tsmem.po.state) + " " + str(tsmem.current_state) + " " + str(tsmem.output_state) - log("smechange set to True because of " + n + " with " + str(tsmem.po.state) + " " + str(tsmem.current_state) + " " + str(tsmem.output_state) ) - - #only for debug - log('## smechange: '+str(smechange)) - log('## eventlist: '+ str(eventlist)) - log('## templateslist: '+str(templateslist.keys())) - log('## serviceslist: '+str(serviceslist.items())) - log('## actions ALL: ' + str(actionlist.keys())) - log('## smechangelist: ' + str(smechangelist.keys())) - - if len(serviceslist)>0: - # would it be a good idea to do some action/event there to stop service being removed ? - # for the moment we do not, logic : if template still there at posttrans, we expand it after removal - # probably the actions in some situations could be done before... - # create an empty temp event - print "Creating temporary event 'temp' and populating it..." - createevent() - print " Adding services to adjust" - for kservices in serviceslist: - log(" " + kservices + ": " + serviceslist[kservices]) - os.symlink(serviceslist[kservices], '/etc/e-smith/events/temp/services2adjust/' + kservices) - - # execute the event ; should we really wait ?? - print "Executing signal-event temp before uninstalling ..........." - os.spawnl(os.P_WAIT,signal_event,signal_event, 'temp') - smeEventPretrans = True; - -def posttrans_hook(conduit): - report_yum_status('posttrans') - log("*******Postrans********") - ts = conduit.getTsInfo() - rpmdb = conduit.getRpmDB() - - global smechange - global eventlist - global actionlist - global templateslist - global serviceslist - global erasing - global smeEventPretrans - global removenorebootok - - - for tsmem in ts.getMembers(): - (n, a, e, v, r) = tsmem.po.pkgtup - # n: name ; a : arch; e: epoch ; v: version, r: release - #if tsmem.output_state in TS_INSTALL_STATES: - # thispo = rpmdb.searchNevra(name=n, arch=a, epoch=e, ver=v, rel=r)[0] - - # we're upgrading/installing/removing a rebootpkgs package.. a reboot is required - for pkg in rebootpkgs: - if n.startswith(pkg): - log('**Package: ' + tsmem.name + ' triggers reboot') - # we do some exclusions -devel -doc ... - if pkg in rebootpkgsexclude: - cont=False - for name in rebootpkgsexclude[pkg]: - if n.startswith(pkg + name): - cont=True - if cont: - # this is an exception we do not need reboot : -devel, -doc ... - continue - # either no exception or does not fit exceptions: we need reboot - smechange = True - smechangelist[n]=str(tsmem.po.state) + " " + str(tsmem.current_state) + " " + str(tsmem.output_state) - log("smechange set to True because of " + n + " with " + str(tsmem.po.state) + " " + str(tsmem.current_state) + " " + str(tsmem.output_state)) - #log("smechange set to True because of " + pkg) - - # check if we're upgrading a restartpkgs rpm - for pkg in restartpkgs: - if n.startswith(pkg): - if pkg in servicenames: - for name in servicenames[pkg]: - if not serviceslist.has_key(name): - serviceslist[name] = 'restart' - else: - if not serviceslist.has_key(pkg): - serviceslist[pkg] = 'restart' - - - #if smechange is true we can ignore the following part - # wondering if we reallly do want to ignore that : let's say we want to delay kernel reboot, - # could we at least configure httpd cleanly until next week ? - # also when updating a package the erasing flag was on and prevented to do this .... - # if not smechange and not erasing: - if True: - - smeevent = tsmem.name + '-update' - if os.path.isdir(events_path + os.sep + smeevent): - tmppath = events_path + os.sep + smeevent + os.sep - eventlist[tsmem.name] = smeevent - - files = [f for f in os.listdir(tmppath)] - for f in files: - if os.path.islink(tmppath + f): - - mytmpaction = os.path.realpath(os.readlink(tmppath + f).replace("..",events_path)) - #log(" link " + f + ": " + mytmpaction) - actionlist[f]=mytmpaction - - - if os.path.isdir(tmppath + "templates2expand" + os.sep): - tmppathtmpl = tmppath + "templates2expand" + os.sep - for subdir, dirs, files in os.walk(tmppathtmpl): - for file in files: - mytmptemplate = os.path.join(subdir, file).replace(tmppathtmpl,os.sep) - if not templateslist.has_key(mytmptemplate): - templateslist[mytmptemplate]=mytmptemplate - log(" template " + mytmptemplate) - - if os.path.isdir(tmppath + "services2adjust" + os.sep): - tmppathsrv = tmppath + "services2adjust" + os.sep - files = [f for f in os.listdir(tmppathsrv)] - for f in files: - if os.path.islink(tmppathsrv + f): - log(" service link " + f + ": " + (os.readlink(tmppathsrv + f))) - mytmpserv = os.readlink(tmppathsrv + f) - if not serviceslist.has_key(f): - serviceslist[f]=mytmpserv - else: - if mytmpserv == "restart": - #only for debug - log("overriding all signals, forcing restart " + f) - serviceslist[f]="restart" - if tsmem.output_state in TS_INSTALL_STATES: - thispo = rpmdb.searchNevra(name=n, arch=a, epoch=e, ver=v, rel=r)[0] - # thispo.dirlist thispo.ghostlist thispo.filelist - if "/etc/e-smith/web/panels/manager/cgi-bin" in thispo.dirlist: - log(" ==> pannel detected : adding navigation-conf") - actionlist['S80navigation-conf']="/etc/e-smith/events/actions/navigation-conf" - - else: - if (n.startswith('smeserver') or n.startswith('e-smith')) and not n.startswith('smeserver-locale') and not (n in removenorebootok): - smechange = True - smechangelist[n]=str(tsmem.po.state) + " " + str(tsmem.current_state) + " " + str(tsmem.output_state) - log("smechange set to True because of " + n + " with " + str(tsmem.po.state) + " " + str(tsmem.current_state) + " " + str(tsmem.output_state) ) - - #only for debug - log('## smechange: '+str(smechange)) - log('## eventlist: '+ str(eventlist)) - log('## templateslist: '+str(templateslist.keys())) - log('## serviceslist: '+str(serviceslist.items())) - log('## actions ALL: ' + str(actionlist.keys())) - log('## smechangelist: ' + str(smechangelist.keys())) - - # check if smechange is true or if eventlist has items.. in both cases we must initialize databases - if smechange == True or len(eventlist)>0: - print 'Initializing databases' - os.spawnl(os.P_WAIT, initialize_database, initialize_database) - - # just in case we add systemd-default and systemd-reload - if not actionlist.has_key('S88systemd-default'): - actionlist['S88systemd-default'] = '/etc/e-smith/events/actions/systemd-default' - if not actionlist.has_key('S89systemd-reload'): - actionlist['S89systemd-reload'] = '/etc/e-smith/events/actions/systemd-reload' - - # here we would like to remove duplicates - # those are potential : S??navigation-conf S??systemd-reload S??systemd-default - for act in ('navigation-conf','systemd-reload','systemd-default'): - global iter - iter = 0 - for key in actionlist.keys(): - if key.endswith(act): - iter += 1 - if iter>1: - del actionlist[key] - log("removing duplicate action " + key + " iter " + str(iter)) - - - # now, if smechange is false (no reboot needed), we can execute actions, expand templates and adjust services - - # create an empty temp event - print "Creating temporary event 'temp' and populating it..." - createevent() - - # fill it with our list of actions - if len(actionlist)>0: - print " Adding actions to execute" - for kactions in actionlist: - if os.path.isfile(actionlist[kactions]): - log(" " + kactions) - os.symlink(actionlist[kactions],'/etc/e-smith/events/temp/' + kactions) - - # fill it with our list of templates to expand - if len(templateslist)>0: - print " Adding templates to expand" - for ktemplates in templateslist: - mytemplatedir = '/etc/e-smith/templates/' + ktemplates - mytemplatemeta = '/etc/e-smith/templates.metadata/' + ktemplates - if os.path.exists(mytemplatedir) or os.path.exists(mytemplatemeta): - log(" " + ktemplates) - addtemplate2expand(ktemplates) - - if len(serviceslist)>0: - print " Adding services to adjust" - for kservices in serviceslist: - log(" " + kservices + ": " + serviceslist[kservices]) - os.symlink(serviceslist[kservices], '/etc/e-smith/events/temp/services2adjust/' + kservices) - - # execute the event ; should we really wait ?? - print "Executing signal-event temp ..........." - os.spawnl(os.P_WAIT,signal_event,signal_event, 'temp') - - if smechange: - os.spawnl(os.P_WAIT, config_set, config_set, 'set', 'UnsavedChanges', 'yes') - os.spawnl(os.P_WAIT, navigation_conf, navigation_conf) - syslog.syslog('Needs signal-event post-upgrade; signal-event reboot because of: ' + str(smechangelist.keys()) ) - print "Reload yum db for server-manager" - os.spawnl(os.P_WAIT, systemctl, systemctl, 'restart', 'yum') - -def close_hook(conduit): - if ourfile and os.path.isfile(status_file): - os.unlink(status_file) - - if smechange: - print "\nThe following updates require a server reboot:\n" + str(smechangelist.keys()) - print "\n==============================================================" - print "WARNING: You now need to run BOTH of the following commands" - print "to ensure consistent system state:\n" - print "signal-event post-upgrade; signal-event reboot\n" - print "You should run these commands unless you are certain that" - print "yum made no changes to your system." - print "==============================================================" - diff --git a/smeserver-update.spec b/smeserver-update.spec index 1e84900..f9a9cbb 100644 --- a/smeserver-update.spec +++ b/smeserver-update.spec @@ -2,7 +2,7 @@ Summary: Koozali SME Server rpm updater Name: %{name} %define version 11.0.0 -%define release 4 +%define release 5 Version: %{version} Release: %{release}%{?dist} License: GPL @@ -23,9 +23,7 @@ Requires: dnf-automatic Requires: python3-dnf-plugins-core Requires: python3-dnf-plugin-post-transaction-actions Requires: python3-dnf-plugin-versionlock -#Requires: yum-plugin-fastestmirror -#Requires: yum-plugin-priorities -#Requires: yum-plugin-post-transaction-actions +Requires: perl(File::Slurp) Requires: mailx BuildRequires: smeserver-devtools BuildRequires: python3 @@ -70,6 +68,10 @@ mkdir -p root/etc/yum.smerepos.d %changelog +* Wed Mar 27 2024 Jean-Philippe Pialasse 11.0.0-5.sme +- rewrite smeserver.py plugin [SME: 12113] +- fix migrate template 39VisibleSmeContribs, 45RockyEOL [SME: 12549] + * Mon Mar 25 2024 Jean-Philippe Pialasse 11.0.0-4.sme - initial release of smeserver-update based on smeserver-yum [SME: 12114] * tidy centos repos, add rocky repos, add support for metalinks