VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/testmanager/webui/wuiadmintestbox.py@ 64719

Last change on this file since 64719 was 64719, checked in by vboxsync, 8 years ago

WuiTestBoxList: Adding summary at the bottom of the page (untested).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.4 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: wuiadmintestbox.py 64719 2016-11-19 19:17:11Z vboxsync $
3
4"""
5Test Manager WUI - TestBox.
6"""
7
8__copyright__ = \
9"""
10Copyright (C) 2012-2016 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: 64719 $"
30
31
32# Standard python imports.
33import socket;
34
35# Validation Kit imports.
36from common import utils, webutils;
37from testmanager.webui.wuicontentbase import WuiContentBase, WuiListContentWithActionBase, WuiFormContentBase, WuiLinkBase, \
38 WuiSvnLink, WuiTmLink, WuiSpanText, WuiRawHtml;
39from testmanager.core.db import TMDatabaseConnection;
40from testmanager.core.schedgroup import SchedGroupLogic, SchedGroupData;
41from testmanager.core.testbox import TestBoxData, TestBoxDataEx;
42from testmanager.core.testset import TestSetData;
43from testmanager.core.db import isDbTimestampInfinity;
44
45
46
47class WuiTestBoxDetailsLink(WuiTmLink):
48 """ Test box details link by ID. """
49
50 def __init__(self, idTestBox, sName = WuiContentBase.ksShortDetailsLink, fBracketed = False, tsNow = None):
51 from testmanager.webui.wuiadmin import WuiAdmin;
52 dParams = {
53 WuiAdmin.ksParamAction: WuiAdmin.ksActionTestBoxDetails,
54 TestBoxData.ksParam_idTestBox: idTestBox,
55 };
56 if tsNow is not None:
57 dParams[WuiAdmin.ksParamEffectiveDate] = tsNow; ## ??
58 WuiTmLink.__init__(self, sName, WuiAdmin.ksScriptName, dParams, fBracketed = fBracketed);
59 self.idTestBox = idTestBox;
60
61
62
63class WuiTestBox(WuiFormContentBase):
64 """
65 WUI TestBox Form Content Generator.
66 """
67
68 def __init__(self, oData, sMode, oDisp):
69 if sMode == WuiFormContentBase.ksMode_Add:
70 sTitle = 'Create TextBox';
71 if oData.uuidSystem is not None and len(oData.uuidSystem) > 10:
72 sTitle += ' - ' + oData.uuidSystem;
73 elif sMode == WuiFormContentBase.ksMode_Edit:
74 sTitle = 'Edit TestBox - %s (#%s)' % (oData.sName, oData.idTestBox);
75 else:
76 assert sMode == WuiFormContentBase.ksMode_Show;
77 sTitle = 'TestBox - %s (#%s)' % (oData.sName, oData.idTestBox);
78 WuiFormContentBase.__init__(self, oData, sMode, 'TestBox', oDisp, sTitle);
79
80 # Try enter sName as hostname (no domain) when creating the testbox.
81 if sMode == WuiFormContentBase.ksMode_Add \
82 and self._oData.sName in [None, ''] \
83 and self._oData.ip not in [None, '']:
84 try:
85 (self._oData.sName, _, _) = socket.gethostbyaddr(self._oData.ip);
86 except:
87 pass;
88 offDot = self._oData.sName.find('.');
89 if offDot > 0:
90 self._oData.sName = self._oData.sName[:offDot];
91
92
93 def _populateForm(self, oForm, oData):
94 oForm.addIntRO( TestBoxData.ksParam_idTestBox, oData.idTestBox, 'TestBox ID');
95 oForm.addIntRO( TestBoxData.ksParam_idGenTestBox, oData.idGenTestBox, 'TestBox generation ID');
96 oForm.addTimestampRO(TestBoxData.ksParam_tsEffective, oData.tsEffective, 'Last changed');
97 oForm.addTimestampRO(TestBoxData.ksParam_tsExpire, oData.tsExpire, 'Expires (excl)');
98 oForm.addIntRO( TestBoxData.ksParam_uidAuthor, oData.uidAuthor, 'Changed by UID');
99
100 oForm.addText( TestBoxData.ksParam_ip, oData.ip, 'TestBox IP Address'); ## make read only??
101 oForm.addUuid( TestBoxData.ksParam_uuidSystem, oData.uuidSystem, 'TestBox System/Firmware UUID');
102 oForm.addText( TestBoxData.ksParam_sName, oData.sName, 'TestBox Name');
103 oForm.addText( TestBoxData.ksParam_sDescription, oData.sDescription, 'TestBox Description');
104 oForm.addCheckBox( TestBoxData.ksParam_fEnabled, oData.fEnabled, 'Enabled');
105 oForm.addComboBox( TestBoxData.ksParam_enmLomKind, oData.enmLomKind, 'Lights-out-management',
106 TestBoxData.kaoLomKindDescs);
107 oForm.addText( TestBoxData.ksParam_ipLom, oData.ipLom, 'Lights-out-management IP Address');
108 oForm.addInt( TestBoxData.ksParam_pctScaleTimeout, oData.pctScaleTimeout, 'Timeout scale factor (%)');
109
110 oForm.addListOfSchedGroupsForTestBox(TestBoxDataEx.ksParam_aoInSchedGroups,
111 oData.aoInSchedGroups,
112 SchedGroupLogic(TMDatabaseConnection()).fetchOrderedByName(),
113 'Scheduling Group');
114 # Command, comment and submit button.
115 if self._sMode == WuiFormContentBase.ksMode_Edit:
116 oForm.addComboBox(TestBoxData.ksParam_enmPendingCmd, oData.enmPendingCmd, 'Pending command',
117 TestBoxData.kaoTestBoxCmdDescs);
118 else:
119 oForm.addComboBoxRO(TestBoxData.ksParam_enmPendingCmd, oData.enmPendingCmd, 'Pending command',
120 TestBoxData.kaoTestBoxCmdDescs);
121 oForm.addMultilineText(TestBoxData.ksParam_sComment, oData.sComment, 'Comment');
122 if self._sMode != WuiFormContentBase.ksMode_Show:
123 oForm.addSubmit('Create TestBox' if self._sMode == WuiFormContentBase.ksMode_Add else 'Change TestBox');
124
125 return True;
126
127
128 def _generatePostFormContent(self, oData):
129 from testmanager.webui.wuihlpform import WuiHlpForm;
130
131 oForm = WuiHlpForm('testbox-machine-settable', '', fReadOnly = True);
132 oForm.addTextRO( TestBoxData.ksParam_sOs, oData.sOs, 'TestBox OS');
133 oForm.addTextRO( TestBoxData.ksParam_sOsVersion, oData.sOsVersion, 'TestBox OS version');
134 oForm.addTextRO( TestBoxData.ksParam_sCpuArch, oData.sCpuArch, 'TestBox OS kernel architecture');
135 oForm.addTextRO( TestBoxData.ksParam_sCpuVendor, oData.sCpuVendor, 'TestBox CPU vendor');
136 oForm.addTextRO( TestBoxData.ksParam_sCpuName, oData.sCpuName, 'TestBox CPU name');
137 if oData.lCpuRevision:
138 oForm.addTextRO( TestBoxData.ksParam_lCpuRevision, '%#x' % (oData.lCpuRevision,), 'TestBox CPU revision',
139 sPostHtml = ' (family=%#x model=%#x stepping=%#x)'
140 % (oData.getCpuFamily(), oData.getCpuModel(), oData.getCpuStepping(),),
141 sSubClass = 'long');
142 else:
143 oForm.addLongRO( TestBoxData.ksParam_lCpuRevision, oData.lCpuRevision, 'TestBox CPU revision');
144 oForm.addIntRO( TestBoxData.ksParam_cCpus, oData.cCpus, 'Number of CPUs, cores and threads');
145 oForm.addCheckBoxRO( TestBoxData.ksParam_fCpuHwVirt, oData.fCpuHwVirt, 'VT-x or AMD-V supported');
146 oForm.addCheckBoxRO( TestBoxData.ksParam_fCpuNestedPaging, oData.fCpuNestedPaging, 'Nested paging supported');
147 oForm.addCheckBoxRO( TestBoxData.ksParam_fCpu64BitGuest, oData.fCpu64BitGuest, '64-bit guest supported');
148 oForm.addCheckBoxRO( TestBoxData.ksParam_fChipsetIoMmu, oData.fChipsetIoMmu, 'I/O MMU supported');
149 oForm.addMultilineTextRO(TestBoxData.ksParam_sReport, oData.sReport, 'Hardware/software report');
150 oForm.addLongRO( TestBoxData.ksParam_cMbMemory, oData.cMbMemory, 'Installed RAM size (MB)');
151 oForm.addLongRO( TestBoxData.ksParam_cMbScratch, oData.cMbScratch, 'Available scratch space (MB)');
152 oForm.addIntRO( TestBoxData.ksParam_iTestBoxScriptRev, oData.iTestBoxScriptRev,
153 'TestBox Script SVN revision');
154 sHexVer = oData.formatPythonVersion();
155 oForm.addIntRO( TestBoxData.ksParam_iPythonHexVersion, oData.iPythonHexVersion,
156 'Python version (hex)', sPostHtml = webutils.escapeElem(sHexVer));
157 return [('Machine Only Settables', oForm.finalize()),];
158
159
160
161class WuiTestBoxList(WuiListContentWithActionBase):
162 """
163 WUI TestBox List Content Generator.
164 """
165
166 ## Descriptors for the combo box.
167 kasTestBoxActionDescs = \
168 [ \
169 [ 'none', 'Select an action...', '' ],
170 [ 'enable', 'Enable', '' ],
171 [ 'disable', 'Disable', '' ],
172 TestBoxData.kaoTestBoxCmdDescs[1],
173 TestBoxData.kaoTestBoxCmdDescs[2],
174 TestBoxData.kaoTestBoxCmdDescs[3],
175 TestBoxData.kaoTestBoxCmdDescs[4],
176 TestBoxData.kaoTestBoxCmdDescs[5],
177 ];
178
179 ## Boxes which doesn't report in for more than 15 min are considered dead.
180 kcSecMaxStatusDeltaAlive = 15*60
181
182 def __init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, fnDPrint, oDisp):
183 # type: (list[TestBoxDataForListing], int, int, datetime.datetime, ignore, WuiAdmin) -> None
184 WuiListContentWithActionBase.__init__(self, aoEntries, iPage, cItemsPerPage, tsEffective,
185 sTitle = 'TestBoxes', sId = 'users', fnDPrint = fnDPrint, oDisp = oDisp);
186 self._asColumnHeaders.extend([ 'Name', 'LOM', 'Status', 'Cmd',
187 'Note', 'Script', 'Python', 'Group',
188 'OS', 'CPU', 'Features', 'CPUs', 'RAM', 'Scratch',
189 'Actions' ]);
190 self._asColumnAttribs.extend([ 'align="center"', 'align="center"', 'align="center"', 'align="center"'
191 'align="center"', 'align="center"', 'align="center"', 'align="center"',
192 '', '', '', 'align="left"', 'align="right"', 'align="right"', 'align="right"',
193 'align="center"' ]);
194 self._aoActions = list(self.kasTestBoxActionDescs);
195 self._sAction = oDisp.ksActionTestBoxListPost;
196 self._sCheckboxName = TestBoxData.ksParam_idTestBox;
197
198 def show(self, fShowNavigation = True):
199 """ Adds some stats at the bottom of the page """
200 (sTitle, sBody) = super(WuiTestBoxList, self).show(fShowNavigation);
201
202 # Count boxes in interesting states.
203 if len(self._aoEntries) > 0:
204 cActive = 0;
205 cDead = 0;
206 for oTestBox in self._aoEntries:
207 oDelta = oTestBox.tsCurrent - oTestBox.oStatus.tsUpdated;
208 if oDelta.days <= 0 and oDelta.seconds <= self.kcSecMaxStatusDeltaAlive:
209 if oTestBox.fEnabled:
210 cActive += 1;
211 else:
212 cDead += 1;
213 sBody += '<div id="testboxsummary"><p>\n' \
214 '%s testboxes of which %s are active and %s dead' \
215 '</p></div>\n' \
216 % (len(self._aoEntries), cActive, cDead,)
217 return (sTitle, sBody);
218
219 def _formatListEntry(self, iEntry): # pylint: disable=R0914
220 from testmanager.webui.wuiadmin import WuiAdmin;
221 oEntry = self._aoEntries[iEntry];
222
223 # Lights outs managment.
224 if oEntry.enmLomKind == TestBoxData.ksLomKind_ILOM:
225 aoLom = [ WuiLinkBase('ILOM', 'https://%s/' % (oEntry.ipLom,), fBracketed = False), ];
226 elif oEntry.enmLomKind == TestBoxData.ksLomKind_ELOM:
227 aoLom = [ WuiLinkBase('ELOM', 'http://%s/' % (oEntry.ipLom,), fBracketed = False), ];
228 elif oEntry.enmLomKind == TestBoxData.ksLomKind_AppleXserveLom:
229 aoLom = [ 'Apple LOM' ];
230 elif oEntry.enmLomKind == TestBoxData.ksLomKind_None:
231 aoLom = [ 'none' ];
232 else:
233 aoLom = [ 'Unexpected enmLomKind value "%s"' % (oEntry.enmLomKind,) ];
234 if oEntry.ipLom is not None:
235 if oEntry.enmLomKind in [ TestBoxData.ksLomKind_ILOM, TestBoxData.ksLomKind_ELOM ]:
236 aoLom += [ WuiLinkBase('(ssh)', 'ssh://%s' % (oEntry.ipLom,), fBracketed = False) ];
237 aoLom += [ WuiRawHtml('<br>'), '%s' % (oEntry.ipLom,) ];
238
239 # State and Last seen.
240 if oEntry.oStatus is None:
241 oSeen = WuiSpanText('tmspan-offline', 'Never');
242 oState = '';
243 else:
244 oDelta = oEntry.tsCurrent - oEntry.oStatus.tsUpdated;
245 if oDelta.days <= 0 and oDelta.seconds <= self.kcSecMaxStatusDeltaAlive:
246 oSeen = WuiSpanText('tmspan-online', u'%s\u00a0s\u00a0ago' % (oDelta.days * 24 * 3600 + oDelta.seconds,));
247 else:
248 oSeen = WuiSpanText('tmspan-offline', u'%s' % (self.formatTsShort(oEntry.oStatus.tsUpdated),));
249
250 if oEntry.oStatus.idTestSet is None:
251 oState = str(oEntry.oStatus.enmState);
252 else:
253 from testmanager.webui.wuimain import WuiMain;
254 oState = WuiTmLink(oEntry.oStatus.enmState, WuiMain.ksScriptName, # pylint: disable=R0204
255 { WuiMain.ksParamAction: WuiMain.ksActionTestResultDetails,
256 TestSetData.ksParam_idTestSet: oEntry.oStatus.idTestSet, },
257 sTitle = '#%u' % (oEntry.oStatus.idTestSet,),
258 fBracketed = False);
259 # Comment
260 oComment = self._formatCommentCell(oEntry.sComment);
261
262 # Group links.
263 aoGroups = [];
264 for oInGroup in oEntry.aoInSchedGroups:
265 oSchedGroup = oInGroup.oSchedGroup;
266 aoGroups.append(WuiTmLink(oSchedGroup.sName, WuiAdmin.ksScriptName,
267 { WuiAdmin.ksParamAction: WuiAdmin.ksActionSchedGroupEdit,
268 SchedGroupData.ksParam_idSchedGroup: oSchedGroup.idSchedGroup, },
269 sTitle = '#%u' % (oSchedGroup.idSchedGroup,),
270 fBracketed = len(oEntry.aoInSchedGroups) > 1));
271
272 # Reformat the OS version to take less space.
273 aoOs = [ 'N/A' ];
274 if oEntry.sOs is not None and oEntry.sOsVersion is not None and oEntry.sCpuArch:
275 sOsVersion = oEntry.sOsVersion;
276 if sOsVersion[0] not in [ 'v', 'V', 'r', 'R'] \
277 and sOsVersion[0].isdigit() \
278 and sOsVersion.find('.') in range(4) \
279 and oEntry.sOs in [ 'linux', 'solaris', 'darwin', ]:
280 sOsVersion = 'v' + sOsVersion;
281
282 sVer1 = sOsVersion;
283 sVer2 = None;
284 if oEntry.sOs == 'linux' or oEntry.sOs == 'darwin':
285 iSep = sOsVersion.find(' / ');
286 if iSep > 0:
287 sVer1 = sOsVersion[:iSep].strip();
288 sVer2 = sOsVersion[iSep + 3:].strip();
289 sVer2 = sVer2.replace('Red Hat Enterprise Linux Server', 'RHEL');
290 sVer2 = sVer2.replace('Oracle Linux Server', 'OL');
291 elif oEntry.sOs == 'solaris':
292 iSep = sOsVersion.find(' (');
293 if iSep > 0 and sOsVersion[-1] == ')':
294 sVer1 = sOsVersion[:iSep].strip();
295 sVer2 = sOsVersion[iSep + 2:-1].strip();
296 aoOs = [
297 WuiSpanText('tmspan-osarch', u'%s.%s' % (oEntry.sOs, oEntry.sCpuArch,)),
298 WuiSpanText('tmspan-osver1', sVer1.replace('-', u'\u2011'),),
299 ];
300 if sVer2 is not None:
301 aoOs += [ WuiRawHtml('<br>'), WuiSpanText('tmspan-osver2', sVer2.replace('-', u'\u2011')), ];
302
303 # Format the CPU revision.
304 oCpu = None;
305 if oEntry.lCpuRevision is not None and oEntry.sCpuVendor is not None and oEntry.sCpuName is not None:
306 oCpu = [
307 u'%s (fam:%xh\u00a0m:%xh\u00a0s:%xh)'
308 % (oEntry.sCpuVendor, oEntry.getCpuFamily(), oEntry.getCpuModel(), oEntry.getCpuStepping(),),
309 WuiRawHtml('<br>'),
310 oEntry.sCpuName,
311 ];
312 else:
313 oCpu = [];
314 if oEntry.sCpuVendor is not None:
315 oCpu.append(oEntry.sCpuVendor);
316 if oEntry.lCpuRevision is not None:
317 oCpu.append('%#x' % (oEntry.lCpuRevision,));
318 if oEntry.sCpuName is not None:
319 oCpu.append(oEntry.sCpuName);
320
321 # Stuff cpu vendor and cpu/box features into one field.
322 asFeatures = []
323 if oEntry.fCpuHwVirt is True: asFeatures.append(u'HW\u2011Virt');
324 if oEntry.fCpuNestedPaging is True: asFeatures.append(u'Nested\u2011Paging');
325 if oEntry.fCpu64BitGuest is True: asFeatures.append(u'64\u2011bit\u2011Guest');
326 if oEntry.fChipsetIoMmu is True: asFeatures.append(u'I/O\u2011MMU');
327 sFeatures = u' '.join(asFeatures) if len(asFeatures) > 0 else u'';
328
329 # Collection applicable actions.
330 aoActions = [
331 WuiTmLink('Details', WuiAdmin.ksScriptName,
332 { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestBoxDetails,
333 TestBoxData.ksParam_idTestBox: oEntry.idTestBox,
334 WuiAdmin.ksParamEffectiveDate: self._tsEffectiveDate, } ),
335 ]
336
337 if isDbTimestampInfinity(oEntry.tsExpire):
338 aoActions += [
339 WuiTmLink('Edit', WuiAdmin.ksScriptName,
340 { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestBoxEdit,
341 TestBoxData.ksParam_idTestBox: oEntry.idTestBox, } ),
342 WuiTmLink('Remove', WuiAdmin.ksScriptName,
343 { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestBoxRemovePost,
344 TestBoxData.ksParam_idTestBox: oEntry.idTestBox },
345 sConfirm = 'Are you sure that you want to remove %s (%s)?' % (oEntry.sName, oEntry.ip) ),
346 ]
347
348 if oEntry.sOs not in [ 'win', 'os2', ] and oEntry.ip is not None:
349 aoActions.append(WuiLinkBase('ssh', 'ssh://vbox@%s' % (oEntry.ip,),));
350
351 return [ self._getCheckBoxColumn(iEntry, oEntry.idTestBox),
352 [ WuiSpanText('tmspan-name', oEntry.sName), WuiRawHtml('<br>'), '%s' % (oEntry.ip,),],
353 aoLom,
354 [
355 '' if oEntry.fEnabled else 'disabled / ',
356 oState,
357 WuiRawHtml('<br>'),
358 oSeen,
359 ],
360 oEntry.enmPendingCmd,
361 oComment,
362 WuiSvnLink(oEntry.iTestBoxScriptRev),
363 oEntry.formatPythonVersion(),
364 aoGroups,
365 aoOs,
366 oCpu,
367 sFeatures,
368 oEntry.cCpus if oEntry.cCpus is not None else 'N/A',
369 utils.formatNumberNbsp(oEntry.cMbMemory) + u'\u00a0MB' if oEntry.cMbMemory is not None else 'N/A',
370 utils.formatNumberNbsp(oEntry.cMbScratch) + u'\u00a0MB' if oEntry.cMbScratch is not None else 'N/A',
371 aoActions,
372 ];
373
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