VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/testmanager/batch/filearchiver.py@ 79087

Last change on this file since 79087 was 79087, checked in by vboxsync, 5 years ago

ValKit: New pylint version - cleanup in progress.

  • Property svn:eol-style set to LF
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 10.1 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# $Id: filearchiver.py 79087 2019-06-11 11:58:28Z vboxsync $
4# pylint: disable=line-too-long
5
6"""
7A cronjob that compresses logs and other files, moving them to the
8g_ksZipFileAreaRootDir storage area.
9"""
10
11from __future__ import print_function;
12
13__copyright__ = \
14"""
15Copyright (C) 2012-2019 Oracle Corporation
16
17This file is part of VirtualBox Open Source Edition (OSE), as
18available from http://www.virtualbox.org. This file is free software;
19you can redistribute it and/or modify it under the terms of the GNU
20General Public License (GPL) as published by the Free Software
21Foundation, in version 2 as it comes in the "COPYING" file of the
22VirtualBox OSE distribution. VirtualBox OSE is distributed in the
23hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
24
25The contents of this file may alternatively be used under the terms
26of the Common Development and Distribution License Version 1.0
27(CDDL) only, as it comes in the "COPYING.CDDL" file of the
28VirtualBox OSE distribution, in which case the provisions of the
29CDDL are applicable instead of those of the GPL.
30
31You may elect to license modified versions of this file under the
32terms and conditions of either the GPL or the CDDL or both.
33"""
34__version__ = "$Revision: 79087 $"
35
36# Standard python imports
37import sys
38import os
39from optparse import OptionParser; # pylint: disable=deprecated-module
40import time;
41import zipfile;
42
43# Add Test Manager's modules path
44g_ksTestManagerDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
45sys.path.append(g_ksTestManagerDir)
46
47# Test Manager imports
48from common import utils;
49from testmanager import config;
50from testmanager.core.db import TMDatabaseConnection;
51from testmanager.core.testset import TestSetData, TestSetLogic;
52
53
54
55class FileArchiverBatchJob(object): # pylint: disable=too-few-public-methods
56 """
57 Log+files comp
58 """
59
60 def __init__(self, oOptions):
61 """
62 Parse command line
63 """
64 self.fVerbose = oOptions.fVerbose;
65 self.sSrcDir = config.g_ksFileAreaRootDir;
66 self.sDstDir = config.g_ksZipFileAreaRootDir;
67 #self.oTestSetLogic = TestSetLogic(TMDatabaseConnection(self.dprint if self.fVerbose else None));
68 self.oTestSetLogic = TestSetLogic(TMDatabaseConnection(None));
69 self.fDryRun = oOptions.fDryRun;
70
71 def dprint(self, sText):
72 """ Verbose output. """
73 if self.fVerbose:
74 print(sText);
75 return True;
76
77 def warning(self, sText):
78 """Prints a warning."""
79 print(sText);
80 return True;
81
82 def _processTestSet(self, idTestSet, asFiles, sCurDir):
83 """
84 Worker for processDir.
85 Same return codes as processDir.
86 """
87
88 sBaseFilename = os.path.join(sCurDir, 'TestSet-%d' % (idTestSet,));
89 if sBaseFilename[0:2] == ('.' + os.path.sep):
90 sBaseFilename = sBaseFilename[2:];
91 sSrcFileBase = os.path.join(self.sSrcDir, sBaseFilename + '-');
92
93 #
94 # Skip the file if the test set is still running.
95 # But delete them if the testset is not found.
96 #
97 oTestSet = self.oTestSetLogic.tryFetch(idTestSet);
98 if oTestSet is not None and sBaseFilename != oTestSet.sBaseFilename:
99 self.warning('TestSet %d: Deleting because sBaseFilename differs: "%s" (disk) vs "%s" (db)' \
100 % (idTestSet, sBaseFilename, oTestSet.sBaseFilename,));
101 oTestSet = None;
102
103 if oTestSet is not None:
104 if oTestSet.enmStatus == TestSetData.ksTestStatus_Running:
105 self.dprint('Skipping test set #%d, still running' % (idTestSet,));
106 return True;
107
108 #
109 # If we have a zip file already, don't try recreate it as we might
110 # have had trouble removing the source files.
111 #
112 sDstDirPath = os.path.join(self.sDstDir, sCurDir);
113 sZipFileNm = os.path.join(sDstDirPath, 'TestSet-%d.zip' % (idTestSet,));
114 if not os.path.exists(sZipFileNm):
115 #
116 # Create zip file with all testset files as members.
117 #
118 self.dprint('TestSet %d: Creating %s...' % (idTestSet, sZipFileNm,));
119 if not self.fDryRun:
120
121 if not os.path.exists(sDstDirPath):
122 os.makedirs(sDstDirPath, 0o755);
123
124 utils.noxcptDeleteFile(sZipFileNm + '.tmp');
125 oZipFile = zipfile.ZipFile(sZipFileNm + '.tmp', 'w', zipfile.ZIP_DEFLATED, allowZip64 = True);
126
127 for sFile in asFiles:
128 sSuff = os.path.splitext(sFile)[1];
129 if sSuff in [ '.png', '.webm', '.gz', '.bz2', '.zip', '.mov', '.avi', '.mpg', '.gif', '.jpg' ]:
130 ## @todo Consider storing these files outside the zip if they are a little largish.
131 self.dprint('TestSet %d: Storing %s...' % (idTestSet, sFile));
132 oZipFile.write(sSrcFileBase + sFile, sFile, zipfile.ZIP_STORED);
133 else:
134 self.dprint('TestSet %d: Deflating %s...' % (idTestSet, sFile));
135 oZipFile.write(sSrcFileBase + sFile, sFile, zipfile.ZIP_DEFLATED);
136
137 oZipFile.close();
138
139 #
140 # .zip.tmp -> .zip.
141 #
142 utils.noxcptDeleteFile(sZipFileNm);
143 os.rename(sZipFileNm + '.tmp', sZipFileNm);
144
145 #else: Dry run.
146 else:
147 self.dprint('TestSet %d: zip file exists already (%s)' % (idTestSet, sZipFileNm,));
148
149 #
150 # Delete the files.
151 #
152 fRc = True;
153 if self.fVerbose:
154 self.dprint('TestSet %d: deleting file: %s' % (idTestSet, asFiles));
155 if not self.fDryRun:
156 for sFile in asFiles:
157 if utils.noxcptDeleteFile(sSrcFileBase + sFile) is False:
158 self.warning('TestSet %d: Failed to delete "%s" (%s)' % (idTestSet, sFile, sSrcFileBase + sFile,));
159 fRc = False;
160
161 return fRc;
162
163
164 def processDir(self, sCurDir):
165 """
166 Process the given directory (relative to sSrcDir and sDstDir).
167 Returns success indicator.
168 """
169 if self.fVerbose:
170 self.dprint('processDir: %s' % (sCurDir,));
171
172 #
173 # Sift thought the directory content, collecting subdirectories and
174 # sort relevant files by test set.
175 # Generally there will either be subdirs or there will be files.
176 #
177 asSubDirs = [];
178 dTestSets = {};
179 sCurPath = os.path.abspath(os.path.join(self.sSrcDir, sCurDir));
180 for sFile in os.listdir(sCurPath):
181 if os.path.isdir(os.path.join(sCurPath, sFile)):
182 if sFile not in [ '.', '..' ]:
183 asSubDirs.append(sFile);
184 elif sFile.startswith('TestSet-'):
185 # Parse the file name. ASSUMES 'TestSet-%d-filename' format.
186 iSlash1 = sFile.find('-');
187 iSlash2 = sFile.find('-', iSlash1 + 1);
188 if iSlash2 <= iSlash1:
189 self.warning('Bad filename (1): "%s"' % (sFile,));
190 continue;
191
192 try: idTestSet = int(sFile[(iSlash1 + 1):iSlash2]);
193 except:
194 self.warning('Bad filename (2): "%s"' % (sFile,));
195 if self.fVerbose:
196 self.dprint('\n'.join(utils.getXcptInfo(4)));
197 continue;
198
199 if idTestSet <= 0:
200 self.warning('Bad filename (3): "%s"' % (sFile,));
201 continue;
202
203 if iSlash2 + 2 >= len(sFile):
204 self.warning('Bad filename (4): "%s"' % (sFile,));
205 continue;
206 sName = sFile[(iSlash2 + 1):];
207
208 # Add it.
209 if idTestSet not in dTestSets:
210 dTestSets[idTestSet] = [];
211 asTestSet = dTestSets[idTestSet];
212 asTestSet.append(sName);
213
214 #
215 # Test sets.
216 #
217 fRc = True;
218 for idTestSet in dTestSets:
219 try:
220 if self._processTestSet(idTestSet, dTestSets[idTestSet], sCurDir) is not True:
221 fRc = False;
222 except:
223 self.warning('TestSet %d: Exception in _processTestSet:\n%s' % (idTestSet, '\n'.join(utils.getXcptInfo()),));
224 fRc = False;
225
226 #
227 # Sub dirs.
228 #
229 for sSubDir in asSubDirs:
230 if self.processDir(os.path.join(sCurDir, sSubDir)) is not True:
231 fRc = False;
232
233 #
234 # Try Remove the directory iff it's not '.' and it's been unmodified
235 # for the last 24h (race protection).
236 #
237 if sCurDir != '.':
238 try:
239 fpModTime = float(os.path.getmtime(sCurPath));
240 if fpModTime + (24*3600) <= time.time():
241 if utils.noxcptRmDir(sCurPath) is True:
242 self.dprint('Removed "%s".' % (sCurPath,));
243 except:
244 pass;
245
246 return fRc;
247
248 @staticmethod
249 def main():
250 """ C-style main(). """
251 #
252 # Parse options.
253 #
254 oParser = OptionParser();
255 oParser.add_option('-v', '--verbose', dest = 'fVerbose', action = 'store_true', default = False,
256 help = 'Verbose output.');
257 oParser.add_option('-q', '--quiet', dest = 'fVerbose', action = 'store_false', default = False,
258 help = 'Quiet operation.');
259 oParser.add_option('-d', '--dry-run', dest = 'fDryRun', action = 'store_true', default = False,
260 help = 'Dry run, do not make any changes.');
261 (oOptions, asArgs) = oParser.parse_args()
262 if asArgs != []:
263 oParser.print_help();
264 return 1;
265
266 #
267 # Do the work.
268 #
269 oBatchJob = FileArchiverBatchJob(oOptions);
270 fRc = oBatchJob.processDir('.');
271 return 0 if fRc is True else 1;
272
273if __name__ == '__main__':
274 sys.exit(FileArchiverBatchJob.main());
275
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette