VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/testmanager/webui/wuilogviewer.py@ 82972

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

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 9.7 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: wuilogviewer.py 82968 2020-02-04 10:35:17Z vboxsync $
3
4"""
5Test Manager WUI - Log viewer
6"""
7
8__copyright__ = \
9"""
10Copyright (C) 2012-2020 Oracle Corporation
11
12This file is part of VirtualBox Open Source Edition (OSE), as
13available from http://www.virtualbox.org. This file is free software;
14you can redistribute it and/or modify it under the terms of the GNU
15General Public License (GPL) as published by the Free Software
16Foundation, in version 2 as it comes in the "COPYING" file of the
17VirtualBox OSE distribution. VirtualBox OSE is distributed in the
18hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
19
20The contents of this file may alternatively be used under the terms
21of the Common Development and Distribution License Version 1.0
22(CDDL) only, as it comes in the "COPYING.CDDL" file of the
23VirtualBox OSE distribution, in which case the provisions of the
24CDDL are applicable instead of those of the GPL.
25
26You may elect to license modified versions of this file under the
27terms and conditions of either the GPL or the CDDL or both.
28"""
29__version__ = "$Revision: 82968 $"
30
31# Validation Kit imports.
32from common import webutils;
33from testmanager.core.testset import TestSetData;
34from testmanager.webui.wuicontentbase import WuiContentBase, WuiTmLink;
35from testmanager.webui.wuimain import WuiMain;
36
37
38class WuiLogViewer(WuiContentBase):
39 """Log viewer."""
40
41 def __init__(self, oTestSet, oLogFile, cbChunk, iChunk, aoTimestamps, oDisp = None, fnDPrint = None):
42 WuiContentBase.__init__(self, oDisp = oDisp, fnDPrint = fnDPrint);
43 self._oTestSet = oTestSet;
44 self._oLogFile = oLogFile;
45 self._cbChunk = cbChunk;
46 self._iChunk = iChunk;
47 self._aoTimestamps = aoTimestamps;
48
49 def _generateNavigation(self, cbFile):
50 """Generate the HTML for the log navigation."""
51
52 dParams = {
53 WuiMain.ksParamAction: WuiMain.ksActionViewLog,
54 WuiMain.ksParamLogSetId: self._oTestSet.idTestSet,
55 WuiMain.ksParamLogFileId: self._oLogFile.idTestResultFile,
56 WuiMain.ksParamLogChunkSize: self._cbChunk,
57 WuiMain.ksParamLogChunkNo: self._iChunk,
58 };
59
60 #
61 # The page walker.
62 #
63 dParams2 = dict(dParams);
64 del dParams2[WuiMain.ksParamLogChunkNo];
65 sHrefFmt = '<a href="?%s&%s=%%s" title="%%s">%%s</a>' \
66 % (webutils.encodeUrlParams(dParams2).replace('%', '%%'), WuiMain.ksParamLogChunkNo,);
67 sHtmlWalker = self.genericPageWalker(self._iChunk, (cbFile + self._cbChunk - 1) / self._cbChunk,
68 sHrefFmt, 11, 0, 'chunk');
69
70 #
71 # The chunk size selector.
72 #
73
74 dParams2 = dict(dParams);
75 del dParams2[WuiMain.ksParamLogChunkSize];
76 sHtmlSize = '<form name="ChunkSizeForm" method="GET">\n' \
77 ' Max <select name="%s" onchange="window.location=\'?%s&%s=\' + ' \
78 'this.options[this.selectedIndex].value;" title="Max items per page">\n' \
79 % ( WuiMain.ksParamLogChunkSize, webutils.encodeUrlParams(dParams2), WuiMain.ksParamLogChunkSize,);
80
81 for cbChunk in [ 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152,
82 4194304, 8388608, 16777216 ]:
83 sHtmlSize += ' <option value="%d" %s>%d bytes</option>\n' \
84 % (cbChunk, 'selected="selected"' if cbChunk == self._cbChunk else '', cbChunk);
85 sHtmlSize += ' </select> per page\n' \
86 '</form>\n'
87
88 #
89 # Download links.
90 #
91 oRawLink = WuiTmLink('View Raw', '',
92 { WuiMain.ksParamAction: WuiMain.ksActionGetFile,
93 WuiMain.ksParamGetFileSetId: self._oTestSet.idTestSet,
94 WuiMain.ksParamGetFileId: self._oLogFile.idTestResultFile,
95 WuiMain.ksParamGetFileDownloadIt: False,
96 },
97 sTitle = '%u MiB' % ((cbFile + 1048576 - 1) / 1048576,) );
98 oDownloadLink = WuiTmLink('Download Log', '',
99 { WuiMain.ksParamAction: WuiMain.ksActionGetFile,
100 WuiMain.ksParamGetFileSetId: self._oTestSet.idTestSet,
101 WuiMain.ksParamGetFileId: self._oLogFile.idTestResultFile,
102 WuiMain.ksParamGetFileDownloadIt: True,
103 },
104 sTitle = '%u MiB' % ((cbFile + 1048576 - 1) / 1048576,) );
105 oTestSetLink = WuiTmLink('Test Set', '',
106 { WuiMain.ksParamAction: WuiMain.ksActionTestResultDetails,
107 TestSetData.ksParam_idTestSet: self._oTestSet.idTestSet,
108 });
109
110
111 #
112 # Combine the elements and return.
113 #
114 return '<div class="tmlogviewernavi">\n' \
115 ' <table width=100%>\n' \
116 ' <tr>\n' \
117 ' <td width=20%>\n' \
118 ' ' + oTestSetLink.toHtml() + '\n' \
119 ' ' + oRawLink.toHtml() + '\n' \
120 ' ' + oDownloadLink.toHtml() + '\n' \
121 ' </td>\n' \
122 ' <td width=60% align=center>' + sHtmlWalker + '</td>' \
123 ' <td width=20% align=right>' + sHtmlSize + '</td>\n' \
124 ' </tr>\n' \
125 ' </table>\n' \
126 '</div>\n';
127
128 def _displayLog(self, oFile, offFile, cbFile, aoTimestamps):
129 """Displays the current section of the log file."""
130 from testmanager.core import db;
131
132 def prepCurTs():
133 """ Formats the current timestamp. """
134 if iCurTs < len(aoTimestamps):
135 oTsZulu = db.dbTimestampToZuluDatetime(aoTimestamps[iCurTs]);
136 return (oTsZulu.strftime('%H:%M:%S.%f'), oTsZulu.strftime('%H_%M_%S_%f'));
137 return ('~~|~~|~~|~~~~~~', '~~|~~|~~|~~~~~~'); # ASCII chars with high values. Limit hits.
138
139 def isCurLineAtOrAfterCurTs():
140 """ Checks if the current line starts with a timestamp that is after the current one. """
141 if len(sLine) >= 15 \
142 and sLine[2] == ':' \
143 and sLine[5] == ':' \
144 and sLine[8] == '.' \
145 and sLine[14] in '0123456789':
146 if sLine[:15] >= sCurTs and iCurTs < len(aoTimestamps):
147 return True;
148 return False;
149
150 # Figure the end offset.
151 offEnd = offFile + self._cbChunk;
152 if offEnd > cbFile:
153 offEnd = cbFile;
154
155 #
156 # Here is an annoying thing, we cannot seek in zip file members. So,
157 # since we have to read from the start, we can just as well count line
158 # numbers while we're at it.
159 #
160 iCurTs = 0;
161 (sCurTs, sCurId) = prepCurTs();
162 offCur = 0;
163 iLine = 0;
164 while True:
165 sLine = oFile.readline().decode('utf-8', 'replace');
166 offLine = offCur;
167 iLine += 1;
168 offCur += len(sLine);
169 if offCur >= offFile or not sLine:
170 break;
171 while isCurLineAtOrAfterCurTs():
172 iCurTs += 1;
173 (sCurTs, sCurId) = prepCurTs();
174
175 #
176 # Got to where we wanted, format the chunk.
177 #
178 asLines = ['\n<div class="tmlog">\n<pre>\n', ];
179 while True:
180 # The timestamp IDs.
181 sPrevTs = '';
182 while isCurLineAtOrAfterCurTs():
183 if sPrevTs != sCurTs:
184 asLines.append('<a id="%s"></a>' % (sCurId,));
185 iCurTs += 1;
186 (sCurTs, sCurId) = prepCurTs();
187
188 # The line.
189 asLines.append('<a id="L%d" href="#L%d">%05d</a><a id="O%d"></a>%s\n' \
190 % (iLine, iLine, iLine, offLine, webutils.escapeElem(sLine.rstrip())));
191
192 # next
193 if offCur >= offEnd:
194 break;
195 sLine = oFile.readline().decode('utf-8', 'replace');
196 offLine = offCur;
197 iLine += 1;
198 offCur += len(sLine);
199 if not sLine:
200 break;
201 asLines.append('<pre/></div>\n');
202 return ''.join(asLines);
203
204
205 def show(self):
206 """Shows the log."""
207
208 if self._oLogFile.sDescription not in [ '', None ]:
209 sTitle = '%s - %s' % (self._oLogFile.sFile, self._oLogFile.sDescription);
210 else:
211 sTitle = '%s' % (self._oLogFile.sFile,);
212
213 #
214 # Open the log file. No universal line endings here.
215 #
216 (oFile, oSizeOrError, _) = self._oTestSet.openFile(self._oLogFile.sFile, 'rb');
217 if oFile is None:
218 return (sTitle, '<p>%s</p>\n' % (webutils.escapeElem(oSizeOrError),),);
219 cbFile = oSizeOrError;
220
221 #
222 # Generate the page.
223 #
224
225 # Start with a focus hack.
226 sHtml = '<div id="tmlogoutdiv" tabindex="0">\n' \
227 '<script lang="text/javascript">\n' \
228 'document.getElementById(\'tmlogoutdiv\').focus();\n' \
229 '</script>\n';
230
231 sNaviHtml = self._generateNavigation(cbFile);
232 sHtml += sNaviHtml;
233
234 offFile = self._iChunk * self._cbChunk;
235 if offFile < cbFile:
236 sHtml += self._displayLog(oFile, offFile, cbFile, self._aoTimestamps);
237 sHtml += sNaviHtml;
238 else:
239 sHtml += '<p>End Of File</p>';
240
241 return (sTitle, sHtml);
242
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