VirtualBox

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

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

TestManager: Hacked up some basic testbox sorting during the weekly meeting.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.4 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: wuiadmintestbox.py 65228 2017-01-10 15:39:14Z 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: 65228 $"
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, TestBoxLogic;
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._aaiColumnSorting.extend([
195 (TestBoxLogic.kiSortColumn_sName,),
196 None, # LOM
197 None, # Status
198 None, # Cmd
199 None, # Note
200 (TestBoxLogic.kiSortColumn_iTestBoxScriptRev,),
201 (TestBoxLogic.kiSortColumn_iPythonHexVersion,),
202 None, # Group
203 (TestBoxLogic.kiSortColumn_sOs, TestBoxLogic.kiSortColumn_sOsVersion, TestBoxLogic.kiSortColumn_sCpuArch,),
204 (TestBoxLogic.kiSortColumn_sCpuVendor, TestBoxLogic.kiSortColumn_lCpuRevision,),
205 (TestBoxLogic.kiSortColumn_fCpuNestedPaging,),
206 (TestBoxLogic.kiSortColumn_cCpus,),
207 (TestBoxLogic.kiSortColumn_cMbMemory,),
208 (TestBoxLogic.kiSortColumn_cMbScratch,),
209 None, # Actions
210 ]);
211 assert len(self._aaiColumnSorting) == len(self._asColumnHeaders);
212 self._aoActions = list(self.kasTestBoxActionDescs);
213 self._sAction = oDisp.ksActionTestBoxListPost;
214 self._sCheckboxName = TestBoxData.ksParam_idTestBox;
215
216 def show(self, fShowNavigation = True):
217 """ Adds some stats at the bottom of the page """
218 (sTitle, sBody) = super(WuiTestBoxList, self).show(fShowNavigation);
219
220 # Count boxes in interesting states.
221 if len(self._aoEntries) > 0:
222 cActive = 0;
223 cDead = 0;
224 for oTestBox in self._aoEntries:
225 if oTestBox.oStatus is not None:
226 oDelta = oTestBox.tsCurrent - oTestBox.oStatus.tsUpdated;
227 if oDelta.days <= 0 and oDelta.seconds <= self.kcSecMaxStatusDeltaAlive:
228 if oTestBox.fEnabled:
229 cActive += 1;
230 else:
231 cDead += 1;
232 else:
233 cDead += 1;
234 sBody += '<div id="testboxsummary"><p>\n' \
235 '%s testboxes of which %s are active and %s dead' \
236 '</p></div>\n' \
237 % (len(self._aoEntries), cActive, cDead,)
238 return (sTitle, sBody);
239
240 def _formatListEntry(self, iEntry): # pylint: disable=R0914
241 from testmanager.webui.wuiadmin import WuiAdmin;
242 oEntry = self._aoEntries[iEntry];
243
244 # Lights outs managment.
245 if oEntry.enmLomKind == TestBoxData.ksLomKind_ILOM:
246 aoLom = [ WuiLinkBase('ILOM', 'https://%s/' % (oEntry.ipLom,), fBracketed = False), ];
247 elif oEntry.enmLomKind == TestBoxData.ksLomKind_ELOM:
248 aoLom = [ WuiLinkBase('ELOM', 'http://%s/' % (oEntry.ipLom,), fBracketed = False), ];
249 elif oEntry.enmLomKind == TestBoxData.ksLomKind_AppleXserveLom:
250 aoLom = [ 'Apple LOM' ];
251 elif oEntry.enmLomKind == TestBoxData.ksLomKind_None:
252 aoLom = [ 'none' ];
253 else:
254 aoLom = [ 'Unexpected enmLomKind value "%s"' % (oEntry.enmLomKind,) ];
255 if oEntry.ipLom is not None:
256 if oEntry.enmLomKind in [ TestBoxData.ksLomKind_ILOM, TestBoxData.ksLomKind_ELOM ]:
257 aoLom += [ WuiLinkBase('(ssh)', 'ssh://%s' % (oEntry.ipLom,), fBracketed = False) ];
258 aoLom += [ WuiRawHtml('<br>'), '%s' % (oEntry.ipLom,) ];
259
260 # State and Last seen.
261 if oEntry.oStatus is None:
262 oSeen = WuiSpanText('tmspan-offline', 'Never');
263 oState = '';
264 else:
265 oDelta = oEntry.tsCurrent - oEntry.oStatus.tsUpdated;
266 if oDelta.days <= 0 and oDelta.seconds <= self.kcSecMaxStatusDeltaAlive:
267 oSeen = WuiSpanText('tmspan-online', u'%s\u00a0s\u00a0ago' % (oDelta.days * 24 * 3600 + oDelta.seconds,));
268 else:
269 oSeen = WuiSpanText('tmspan-offline', u'%s' % (self.formatTsShort(oEntry.oStatus.tsUpdated),));
270
271 if oEntry.oStatus.idTestSet is None:
272 oState = str(oEntry.oStatus.enmState);
273 else:
274 from testmanager.webui.wuimain import WuiMain;
275 oState = WuiTmLink(oEntry.oStatus.enmState, WuiMain.ksScriptName, # pylint: disable=R0204
276 { WuiMain.ksParamAction: WuiMain.ksActionTestResultDetails,
277 TestSetData.ksParam_idTestSet: oEntry.oStatus.idTestSet, },
278 sTitle = '#%u' % (oEntry.oStatus.idTestSet,),
279 fBracketed = False);
280 # Comment
281 oComment = self._formatCommentCell(oEntry.sComment);
282
283 # Group links.
284 aoGroups = [];
285 for oInGroup in oEntry.aoInSchedGroups:
286 oSchedGroup = oInGroup.oSchedGroup;
287 aoGroups.append(WuiTmLink(oSchedGroup.sName, WuiAdmin.ksScriptName,
288 { WuiAdmin.ksParamAction: WuiAdmin.ksActionSchedGroupEdit,
289 SchedGroupData.ksParam_idSchedGroup: oSchedGroup.idSchedGroup, },
290 sTitle = '#%u' % (oSchedGroup.idSchedGroup,),
291 fBracketed = len(oEntry.aoInSchedGroups) > 1));
292
293 # Reformat the OS version to take less space.
294 aoOs = [ 'N/A' ];
295 if oEntry.sOs is not None and oEntry.sOsVersion is not None and oEntry.sCpuArch:
296 sOsVersion = oEntry.sOsVersion;
297 if sOsVersion[0] not in [ 'v', 'V', 'r', 'R'] \
298 and sOsVersion[0].isdigit() \
299 and sOsVersion.find('.') in range(4) \
300 and oEntry.sOs in [ 'linux', 'solaris', 'darwin', ]:
301 sOsVersion = 'v' + sOsVersion;
302
303 sVer1 = sOsVersion;
304 sVer2 = None;
305 if oEntry.sOs == 'linux' or oEntry.sOs == 'darwin':
306 iSep = sOsVersion.find(' / ');
307 if iSep > 0:
308 sVer1 = sOsVersion[:iSep].strip();
309 sVer2 = sOsVersion[iSep + 3:].strip();
310 sVer2 = sVer2.replace('Red Hat Enterprise Linux Server', 'RHEL');
311 sVer2 = sVer2.replace('Oracle Linux Server', 'OL');
312 elif oEntry.sOs == 'solaris':
313 iSep = sOsVersion.find(' (');
314 if iSep > 0 and sOsVersion[-1] == ')':
315 sVer1 = sOsVersion[:iSep].strip();
316 sVer2 = sOsVersion[iSep + 2:-1].strip();
317 aoOs = [
318 WuiSpanText('tmspan-osarch', u'%s.%s' % (oEntry.sOs, oEntry.sCpuArch,)),
319 WuiSpanText('tmspan-osver1', sVer1.replace('-', u'\u2011'),),
320 ];
321 if sVer2 is not None:
322 aoOs += [ WuiRawHtml('<br>'), WuiSpanText('tmspan-osver2', sVer2.replace('-', u'\u2011')), ];
323
324 # Format the CPU revision.
325 oCpu = None;
326 if oEntry.lCpuRevision is not None and oEntry.sCpuVendor is not None and oEntry.sCpuName is not None:
327 oCpu = [
328 u'%s (fam:%xh\u00a0m:%xh\u00a0s:%xh)'
329 % (oEntry.sCpuVendor, oEntry.getCpuFamily(), oEntry.getCpuModel(), oEntry.getCpuStepping(),),
330 WuiRawHtml('<br>'),
331 oEntry.sCpuName,
332 ];
333 else:
334 oCpu = [];
335 if oEntry.sCpuVendor is not None:
336 oCpu.append(oEntry.sCpuVendor);
337 if oEntry.lCpuRevision is not None:
338 oCpu.append('%#x' % (oEntry.lCpuRevision,));
339 if oEntry.sCpuName is not None:
340 oCpu.append(oEntry.sCpuName);
341
342 # Stuff cpu vendor and cpu/box features into one field.
343 asFeatures = []
344 if oEntry.fCpuHwVirt is True: asFeatures.append(u'HW\u2011Virt');
345 if oEntry.fCpuNestedPaging is True: asFeatures.append(u'Nested\u2011Paging');
346 if oEntry.fCpu64BitGuest is True: asFeatures.append(u'64\u2011bit\u2011Guest');
347 if oEntry.fChipsetIoMmu is True: asFeatures.append(u'I/O\u2011MMU');
348 sFeatures = u' '.join(asFeatures) if len(asFeatures) > 0 else u'';
349
350 # Collection applicable actions.
351 aoActions = [
352 WuiTmLink('Details', WuiAdmin.ksScriptName,
353 { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestBoxDetails,
354 TestBoxData.ksParam_idTestBox: oEntry.idTestBox,
355 WuiAdmin.ksParamEffectiveDate: self._tsEffectiveDate, } ),
356 ]
357
358 if isDbTimestampInfinity(oEntry.tsExpire):
359 aoActions += [
360 WuiTmLink('Edit', WuiAdmin.ksScriptName,
361 { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestBoxEdit,
362 TestBoxData.ksParam_idTestBox: oEntry.idTestBox, } ),
363 WuiTmLink('Remove', WuiAdmin.ksScriptName,
364 { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestBoxRemovePost,
365 TestBoxData.ksParam_idTestBox: oEntry.idTestBox },
366 sConfirm = 'Are you sure that you want to remove %s (%s)?' % (oEntry.sName, oEntry.ip) ),
367 ]
368
369 if oEntry.sOs not in [ 'win', 'os2', ] and oEntry.ip is not None:
370 aoActions.append(WuiLinkBase('ssh', 'ssh://vbox@%s' % (oEntry.ip,),));
371
372 return [ self._getCheckBoxColumn(iEntry, oEntry.idTestBox),
373 [ WuiSpanText('tmspan-name', oEntry.sName), WuiRawHtml('<br>'), '%s' % (oEntry.ip,),],
374 aoLom,
375 [
376 '' if oEntry.fEnabled else 'disabled / ',
377 oState,
378 WuiRawHtml('<br>'),
379 oSeen,
380 ],
381 oEntry.enmPendingCmd,
382 oComment,
383 WuiSvnLink(oEntry.iTestBoxScriptRev),
384 oEntry.formatPythonVersion(),
385 aoGroups,
386 aoOs,
387 oCpu,
388 sFeatures,
389 oEntry.cCpus if oEntry.cCpus is not None else 'N/A',
390 utils.formatNumberNbsp(oEntry.cMbMemory) + u'\u00a0MB' if oEntry.cMbMemory is not None else 'N/A',
391 utils.formatNumberNbsp(oEntry.cMbScratch) + u'\u00a0MB' if oEntry.cMbScratch is not None else 'N/A',
392 aoActions,
393 ];
394
Note: See TracBrowser for help on using the repository browser.

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