|
44 |
from string import join |
44 |
from string import join |
45 |
from subprocess import Popen |
45 |
from subprocess import Popen |
46 |
from hashlib import md5 |
46 |
from hashlib import md5 |
|
|
47 |
from copy import deepcopy |
47 |
|
48 |
|
48 |
from univention.management.console.log import MODULE |
49 |
from univention.management.console.log import MODULE |
49 |
from univention.management.console.protocol.definitions import * |
50 |
from univention.management.console.protocol.definitions import * |
|
104 |
'logfile': '/var/log/univention/updater.log', |
105 |
'logfile': '/var/log/univention/updater.log', |
105 |
'statusfile': '/var/lib/univention-updater/univention-updater.status' |
106 |
'statusfile': '/var/lib/univention-updater/univention-updater.status' |
106 |
}, |
107 |
}, |
107 |
# no API available, and no wrapper-wrapper too (or at least, didn't find one). |
108 |
# *** IMPORTANT! *** the arg list from our request contains the COMPONENT name but the command |
108 |
# Should I write a wrapper, especially to get consistent behaviour in terms of 'logfile' and 'statusfile'? |
109 |
# here must contain the list of DEFAULTPACKAGES! |
109 |
# cmd = '/usr/share/univention-updater/univention-updater-umc-univention-install %s' % (' '.join(pkglist)) |
110 |
# cmd = '/usr/share/univention-updater/univention-updater-umc-univention-install %s' % (' '.join(pkglist)) |
110 |
'component': { |
111 |
'component': { |
111 |
'purpose': _("Install component '%s'"), |
112 |
'purpose': _("Install component '%s'"), |
|
843 |
MODULE.info(" << %s" % s) |
844 |
MODULE.info(" << %s" % s) |
844 |
# ----------------------------------- |
845 |
# ----------------------------------- |
845 |
result = None |
846 |
result = None |
846 |
job = 'unknown' |
847 |
job = '' |
847 |
if self._current_job and 'job' in self._current_job: |
848 |
if self._current_job and 'job' in self._current_job: |
848 |
job = self._current_job['job'] |
849 |
job = self._current_job['job'] |
849 |
else: |
850 |
else: |
850 |
job = request.options.get('job','none') |
851 |
job = request.options.get('job','') |
851 |
|
852 |
|
852 |
count = request.options.get('count',0) |
853 |
count = request.options.get('count',0) |
853 |
result = 0 if count < 0 else [] |
854 |
result = 0 if count < 0 else [] |
854 |
if not job in INSTALLERS: |
855 |
if not job in INSTALLERS: |
855 |
MODULE.warn(" ?? Don't know a '%s' job" % job) |
856 |
# job empty: this is the first call I can't avoid |
|
|
857 |
if job != '': |
858 |
MODULE.warn(" ?? Don't know a '%s' job" % job) |
856 |
else: |
859 |
else: |
857 |
if not 'logfile' in INSTALLERS[job]: |
860 |
if not 'logfile' in INSTALLERS[job]: |
858 |
MODULE.warn(" ?? Job '%s' has no associated log file" % job) |
861 |
MODULE.warn(" ?? Job '%s' has no associated log file" % job) |
859 |
else: |
862 |
else: |
860 |
fname = INSTALLERS[job]['logfile'] |
863 |
fname = INSTALLERS[job]['logfile'] |
861 |
result = self._logview(fname, count) |
864 |
if count < 0: |
|
|
865 |
result = self._logstamp(fname) |
866 |
else: |
867 |
# don't read complete file if we have an 'ignore' count |
868 |
if (count == 0) and (self._current_job['lines']): |
869 |
count = -self._current_job['lines'] |
870 |
result = self._logview(fname, count) |
862 |
|
871 |
|
863 |
# again debug, shortened |
872 |
# again debug, shortened |
864 |
if isinstance(result,int): |
873 |
if isinstance(result,int): |
|
900 |
job = request.options.get('job','') |
909 |
job = request.options.get('job','') |
901 |
result = {} |
910 |
result = {} |
902 |
if job in INSTALLERS: |
911 |
if job in INSTALLERS: |
903 |
result = INSTALLERS[job] |
912 |
# make a copy, not a reference! |
|
|
913 |
# result = {} |
914 |
# for arg in INSTALLERS[job]: |
915 |
# result[arg] = INSTALLERS[job][arg] |
916 |
result = deepcopy(INSTALLERS[job]) |
917 |
|
904 |
if 'statusfile' in INSTALLERS[job]: |
918 |
if 'statusfile' in INSTALLERS[job]: |
905 |
try: |
919 |
try: |
906 |
for line in open(INSTALLERS[job]['statusfile']): |
920 |
for line in open(INSTALLERS[job]['statusfile']): |
|
944 |
result['label'] = result['purpose'] % result['detail'] |
958 |
result['label'] = result['purpose'] % result['detail'] |
945 |
else: |
959 |
else: |
946 |
result['label'] = result['purpose'] |
960 |
result['label'] = result['purpose'] |
|
|
961 |
# Affordance to reboot... hopefully this gets set before |
962 |
# we stop polling on this job status |
963 |
self.ucr.load() # make it as current as possible |
964 |
result['reboot'] = self.ucr.is_true('update/reboot/required',False) |
947 |
|
965 |
|
948 |
# ----------- DEBUG ----------------- |
966 |
# ----------- DEBUG ----------------- |
949 |
MODULE.info("online/installer/status returns:") |
967 |
MODULE.info("online/installer/status returns:") |
|
979 |
MODULE.info(" << %s" % s) |
997 |
MODULE.info(" << %s" % s) |
980 |
# ----------------------------------- |
998 |
# ----------------------------------- |
981 |
|
999 |
|
|
|
1000 |
# Clean up any stored job details ... they're now obsolete. |
1001 |
self._current_job = {} |
1002 |
|
982 |
result = {} |
1003 |
result = {} |
983 |
result['status'] = 0 # successful. If not: set result['message'] too. |
1004 |
result['status'] = 0 # successful. If not: set result['message'] too. |
984 |
|
1005 |
|
|
999 |
self.finished(request.id,result) |
1020 |
self.finished(request.id,result) |
1000 |
return |
1021 |
return |
1001 |
|
1022 |
|
1002 |
cmd = INSTALLERS[subject]['command'] |
1023 |
# We want to limit the amount of logfile data being transferred |
1003 |
if cmd.find('%') != -1: |
1024 |
# to the frontend. So we remember the line count of the associated |
1004 |
cmd = cmd % request.options.get('detail','') |
1025 |
# log file. |
1005 |
MODULE.info(" ++ Creating job: '%s'" % cmd) |
1026 |
if 'logfile' in INSTALLERS[subject]: |
1006 |
self.__create_at_job(cmd,detail) |
1027 |
fname = INSTALLERS[subject]['logfile'] |
|
|
1028 |
count = 0 |
1029 |
try: |
1030 |
file = open(fname,'r') |
1031 |
count = 0 |
1032 |
for line in file: |
1033 |
count += 1 |
1034 |
finally: |
1035 |
if file != None: |
1036 |
file.close() |
1037 |
self._current_job['lines'] = count |
1007 |
|
1038 |
|
|
|
1039 |
try: |
1040 |
# Assemble the command line, now somewhat complicated: |
1041 |
# |
1042 |
# (1) take the 'command' entry from the INSTALLERS entry of this subject |
1043 |
# (2) if it doesn't contain a percent sign -> ready. |
1044 |
# (3) if it contains a percent sign: we must format something: |
1045 |
# (4) if the subject is about 'component' we must get the 'defaultpackages' |
1046 |
# entry from the UCR tuple named by 'detail' and use that. |
1047 |
# (5) if not, we can format the 'detail' field into the command. |
1048 |
# cmd = '%s' % INSTALLERS[subject]['command'] # I need a copy of this string! |
1049 |
cmd = INSTALLERS[subject]['command'] |
1050 |
if cmd.find('%') != -1: |
1051 |
if subject == 'component': |
1052 |
# Strictly spoken, we can't arrive here if 'defaultpackages' is not set |
1053 |
ucrs = '%s/%s/defaultpackages' % (COMPONENT_BASE,detail) |
1054 |
pkgs = self.ucr.get(ucrs,'') |
1055 |
cmd = cmd % pkgs |
1056 |
MODULE.info(" Resolution of default packages of the '%s' component:" % detail) |
1057 |
MODULE.info(" UCRS = '%s'" % ucrs) |
1058 |
MODULE.info(" PKGS = '%s'" % pkgs) |
1059 |
MODULE.info(" CMD = '%s'" % cmd) |
1060 |
else: |
1061 |
cmd = cmd % request.options.get('detail','') |
1062 |
MODULE.info(" ++ Creating job: '%s'" % cmd) |
1063 |
self.__create_at_job(cmd,detail) |
1064 |
except Exception,ex: |
1065 |
MODULE.warn(" ERROR: %s" % str(ex)) |
1066 |
|
1008 |
# ----------- DEBUG ----------------- |
1067 |
# ----------- DEBUG ----------------- |
1009 |
MODULE.info("online/installer/execute returns:") |
1068 |
MODULE.info("online/installer/execute returns:") |
1010 |
pp = pprint.PrettyPrinter(indent=4) |
1069 |
pp = pprint.PrettyPrinter(indent=4) |
|
1243 |
# |
1302 |
# |
1244 |
# ------------------------------------------------------------------------------ |
1303 |
# ------------------------------------------------------------------------------ |
1245 |
|
1304 |
|
|
|
1305 |
def _logstamp(self,fname): |
1306 |
""" Logfile timestamp. Now a seperate function. |
1307 |
""" |
1308 |
try: |
1309 |
st = stat(fname) |
1310 |
if st: |
1311 |
MODULE.info(" >> log file stamp = '%s'" % st[9]) |
1312 |
return st[9] |
1313 |
return 0 |
1314 |
except: |
1315 |
return 0 |
1316 |
|
1246 |
def _logview(self,fname,count): |
1317 |
def _logview(self,fname,count): |
1247 |
"""Contains all functions needed to view or 'tail' an arbitrary text file. |
1318 |
""" Contains all functions needed to view or 'tail' an arbitrary text file. |
1248 |
Argument 'count' can have different values: |
1319 |
Argument 'count' can have different values: |
1249 |
< 0 ... return Unix timestamp of log file, to avoid fetching unchanged file. |
1320 |
< 0 ... ignore this many lines, return the rest of the file |
1250 |
0 ..... return the whole file, splitted into lines. |
1321 |
0 ..... return the whole file, splitted into lines. |
1251 |
> 0 ... return the last 'count' lines of the file. (a.k.a. tail -n <count>)""" |
1322 |
> 0 ... return the last 'count' lines of the file. (a.k.a. tail -n <count>) |
|
|
1323 |
""" |
1252 |
lines = [] |
1324 |
lines = [] |
1253 |
if count < 0: |
|
|
1254 |
try: |
1255 |
st = stat(fname) |
1256 |
if st: |
1257 |
MODULE.info(" >> log file stamp = '%s'" % st[9]) |
1258 |
return st[9] |
1259 |
return 0 |
1260 |
except: |
1261 |
return 0 |
1262 |
try: |
1325 |
try: |
1263 |
file = open(fname,'r') |
1326 |
file = open(fname,'r') |
1264 |
for line in file: |
1327 |
for line in file: |
1265 |
l = line.rstrip() |
1328 |
if (count < 0): |
1266 |
lines.append(l) |
1329 |
count += 1 |
1267 |
if (count) and (len(lines) > count): |
1330 |
else: |
1268 |
lines.pop(0) |
1331 |
l = line.rstrip() |
|
|
1332 |
lines.append(l) |
1333 |
if (count > 0) and (len(lines) > count): |
1334 |
lines.pop(0) |
1269 |
finally: |
1335 |
finally: |
1270 |
if file != None: |
1336 |
if file != None: |
1271 |
file.close() |
1337 |
file.close() |
|
1289 |
script = ''' |
1355 |
script = ''' |
1290 |
#:started: %s |
1356 |
#:started: %s |
1291 |
#:detail: %s |
1357 |
#:detail: %s |
|
|
1358 |
#:command: %s |
1292 |
dpkg-statoverride --add root root 0644 /usr/sbin/univention-management-console-web-server |
1359 |
dpkg-statoverride --add root root 0644 /usr/sbin/univention-management-console-web-server |
1293 |
dpkg-statoverride --add root root 0644 /usr/sbin/univention-management-console-server |
1360 |
dpkg-statoverride --add root root 0644 /usr/sbin/univention-management-console-server |
1294 |
dpkg-statoverride --add root root 0644 /usr/sbin/apache2 |
1361 |
dpkg-statoverride --add root root 0644 /usr/sbin/apache2 |
|
1301 |
dpkg-statoverride --remove /usr/sbin/univention-management-console-server |
1368 |
dpkg-statoverride --remove /usr/sbin/univention-management-console-server |
1302 |
dpkg-statoverride --remove /usr/sbin/apache2 |
1369 |
dpkg-statoverride --remove /usr/sbin/apache2 |
1303 |
chmod +x /usr/sbin/univention-management-console-server /usr/sbin/univention-management-console-web-server /usr/sbin/apache2 |
1370 |
chmod +x /usr/sbin/univention-management-console-server /usr/sbin/univention-management-console-web-server /usr/sbin/apache2 |
1304 |
''' % (started,detail,command) |
1371 |
''' % (started,detail,command,command) |
1305 |
p1 = subprocess.Popen( [ 'LC_ALL=C at now', ], stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell = True ) |
1372 |
p1 = subprocess.Popen( [ 'LC_ALL=C at now', ], stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell = True ) |
1306 |
(stdout,stderr) = p1.communicate( script ) |
1373 |
(stdout,stderr) = p1.communicate( script ) |
1307 |
|
1374 |
|
|
1330 |
cmd = INSTALLERS[inst]['command'].split('%')[0] |
1397 |
cmd = INSTALLERS[inst]['command'].split('%')[0] |
1331 |
MODULE.info(" ++ Checking for '%s'" % cmd) |
1398 |
MODULE.info(" ++ Checking for '%s'" % cmd) |
1332 |
if cmd in atout: |
1399 |
if cmd in atout: |
1333 |
self._current_job = {} |
1400 |
# cleaning up is done in 'run_installer()' |
|
|
1401 |
# self._current_job = {} |
1334 |
self._current_job['job'] = inst # job key |
1402 |
self._current_job['job'] = inst # job key |
1335 |
self._current_job['running'] = True # currently running: we have found it per 'at' job |
1403 |
self._current_job['running'] = True # currently running: we have found it per 'at' job |
1336 |
self._current_job['time'] = int(time()) # record the last time we've seen this job |
1404 |
self._current_job['time'] = int(time()) # record the last time we've seen this job |