VirtualBox

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