VirtualBox

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

Last change on this file since 61503 was 61502, checked in by vboxsync, 9 years ago

testmanager: Testboxes can now be members of more than one scheduling group.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.9 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: wuiadmintestbox.py 61502 2016-06-06 17:53:01Z vboxsync $
3
4"""
5Test Manager WUI - TestBox.
6"""
7
8__copyright__ = \
9"""
10Copyright (C) 2012-2015 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: 61502 $"
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 def __init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, fnDPrint, oDisp):
180 # type: (list[TestBoxDataForListing], int, int, datetime.datetime, ignore, WuiAdmin) -> None
181 WuiListContentWithActionBase.__init__(self, aoEntries, iPage, cItemsPerPage, tsEffective,
182 sTitle = 'TestBoxes', sId = 'users', fnDPrint = fnDPrint, oDisp = oDisp);
183 self._asColumnHeaders.extend([ 'Name', 'LOM', 'Status', 'Cmd',
184 'Note', 'Script', 'Python', 'Group',
185 'OS', 'CPU', 'Features', 'CPUs', 'RAM', 'Scratch',
186 'Actions' ]);
187 self._asColumnAttribs.extend([ 'align="center"', 'align="center"', 'align="center"', 'align="center"'
188 'align="center"', 'align="center"', 'align="center"', 'align="center"',
189 '', '', '', 'align="left"', 'align="right"', 'align="right"', 'align="right"',
190 'align="center"' ]);
191 self._aoActions = list(self.kasTestBoxActionDescs);
192 self._aoSchedGroups = SchedGroupLogic(self._oDisp.getDb()).fetchOrderedByName();
193 self._dSchedGroups = dict();
194 for oSchedGroup in self._aoSchedGroups:
195 self._aoActions.append([ 'setgroup-%u' % (oSchedGroup.idSchedGroup,),
196 'Migrate to group %s (#%u)' % (oSchedGroup.sName, oSchedGroup.idSchedGroup,),
197 oSchedGroup.sDescription ]);
198 self._dSchedGroups[oSchedGroup.idSchedGroup] = oSchedGroup;
199 self._sAction = oDisp.ksActionTestBoxListPost;
200 self._sCheckboxName = TestBoxData.ksParam_idTestBox;
201
202 def _formatListEntry(self, iEntry): # pylint: disable=R0914
203 from testmanager.webui.wuiadmin import WuiAdmin;
204 oEntry = self._aoEntries[iEntry];
205
206 # Lights outs managment.
207 if oEntry.enmLomKind == TestBoxData.ksLomKind_ILOM:
208 aoLom = [ WuiLinkBase('ILOM', 'https://%s/' % (oEntry.ipLom,), fBracketed = False), ];
209 elif oEntry.enmLomKind == TestBoxData.ksLomKind_ELOM:
210 aoLom = [ WuiLinkBase('ELOM', 'http://%s/' % (oEntry.ipLom,), fBracketed = False), ];
211 elif oEntry.enmLomKind == TestBoxData.ksLomKind_AppleXserveLom:
212 aoLom = [ 'Apple LOM' ];
213 elif oEntry.enmLomKind == TestBoxData.ksLomKind_None:
214 aoLom = [ 'none' ];
215 else:
216 aoLom = [ 'Unexpected enmLomKind value "%s"' % (oEntry.enmLomKind,) ];
217 if oEntry.ipLom is not None:
218 if oEntry.enmLomKind in [ TestBoxData.ksLomKind_ILOM, TestBoxData.ksLomKind_ELOM ]:
219 aoLom += [ WuiLinkBase('(ssh)', 'ssh://%s' % (oEntry.ipLom,), fBracketed = False) ];
220 aoLom += [ WuiRawHtml('<br>'), '%s' % (oEntry.ipLom,) ];
221
222 # State and Last seen.
223 if oEntry.oStatus is None:
224 oSeen = WuiSpanText('tmspan-offline', 'Never');
225 oState = '';
226 else:
227 oDelta = oEntry.tsCurrent - oEntry.oStatus.tsUpdated;
228 if oDelta.days <= 0 and oDelta.seconds <= 15*60: # 15 mins and we consider you dead.
229 oSeen = WuiSpanText('tmspan-online', u'%s\u00a0s\u00a0ago' % (oDelta.days * 24 * 3600 + oDelta.seconds,));
230 else:
231 oSeen = WuiSpanText('tmspan-offline', u'%s' % (self.formatTsShort(oEntry.oStatus.tsUpdated),));
232
233 if oEntry.oStatus.idTestSet is None:
234 oState = str(oEntry.oStatus.enmState);
235 else:
236 from testmanager.webui.wuimain import WuiMain;
237 oState = WuiTmLink(oEntry.oStatus.enmState, WuiMain.ksScriptName, # pylint: disable=R0204
238 { WuiMain.ksParamAction: WuiMain.ksActionTestResultDetails,
239 TestSetData.ksParam_idTestSet: oEntry.oStatus.idTestSet, },
240 sTitle = '#%u' % (oEntry.oStatus.idTestSet,),
241 fBracketed = False);
242 # Comment
243 oComment = self._formatCommentCell(oEntry.sComment);
244
245 # Group links.
246 aoGroups = [];
247 for oInGroup in oEntry.aoInSchedGroups:
248 oSchedGroup = oInGroup.oSchedGroup;
249 aoGroups.append(WuiTmLink(oSchedGroup.sName, WuiAdmin.ksScriptName,
250 { WuiAdmin.ksParamAction: WuiAdmin.ksActionSchedGroupEdit,
251 SchedGroupData.ksParam_idSchedGroup: oSchedGroup.idSchedGroup, },
252 sTitle = '#%u' % (oSchedGroup.idSchedGroup,),
253 fBracketed = len(oEntry.aoInSchedGroups) > 1));
254
255 # Reformat the OS version to take less space.
256 aoOs = [ 'N/A' ];
257 if oEntry.sOs is not None and oEntry.sOsVersion is not None and oEntry.sCpuArch:
258 sOsVersion = oEntry.sOsVersion;
259 if sOsVersion[0] not in [ 'v', 'V', 'r', 'R'] \
260 and sOsVersion[0].isdigit() \
261 and sOsVersion.find('.') in range(4) \
262 and oEntry.sOs in [ 'linux', 'solaris', 'darwin', ]:
263 sOsVersion = 'v' + sOsVersion;
264
265 sVer1 = sOsVersion;
266 sVer2 = None;
267 if oEntry.sOs == 'linux' or oEntry.sOs == 'darwin':
268 iSep = sOsVersion.find(' / ');
269 if iSep > 0:
270 sVer1 = sOsVersion[:iSep].strip();
271 sVer2 = sOsVersion[iSep + 3:].strip();
272 sVer2 = sVer2.replace('Red Hat Enterprise Linux Server', 'RHEL');
273 sVer2 = sVer2.replace('Oracle Linux Server', 'OL');
274 elif oEntry.sOs == 'solaris':
275 iSep = sOsVersion.find(' (');
276 if iSep > 0 and sOsVersion[-1] == ')':
277 sVer1 = sOsVersion[:iSep].strip();
278 sVer2 = sOsVersion[iSep + 2:-1].strip();
279 aoOs = [
280 WuiSpanText('tmspan-osarch', u'%s.%s' % (oEntry.sOs, oEntry.sCpuArch,)),
281 WuiSpanText('tmspan-osver1', sVer1.replace('-', u'\u2011'),),
282 ];
283 if sVer2 is not None:
284 aoOs += [ WuiRawHtml('<br>'), WuiSpanText('tmspan-osver2', sVer2.replace('-', u'\u2011')), ];
285
286 # Format the CPU revision.
287 oCpu = None;
288 if oEntry.lCpuRevision is not None and oEntry.sCpuVendor is not None and oEntry.sCpuName is not None:
289 oCpu = [
290 u'%s (fam:%xh\u00a0m:%xh\u00a0s:%xh)'
291 % (oEntry.sCpuVendor, oEntry.getCpuFamily(), oEntry.getCpuModel(), oEntry.getCpuStepping(),),
292 WuiRawHtml('<br>'),
293 oEntry.sCpuName,
294 ];
295 else:
296 oCpu = [];
297 if oEntry.sCpuVendor is not None:
298 oCpu.append(oEntry.sCpuVendor);
299 if oEntry.lCpuRevision is not None:
300 oCpu.append('%#x' % (oEntry.lCpuRevision,));
301 if oEntry.sCpuName is not None:
302 oCpu.append(oEntry.sCpuName);
303
304 # Stuff cpu vendor and cpu/box features into one field.
305 asFeatures = []
306 if oEntry.fCpuHwVirt is True: asFeatures.append(u'HW\u2011Virt');
307 if oEntry.fCpuNestedPaging is True: asFeatures.append(u'Nested\u2011Paging');
308 if oEntry.fCpu64BitGuest is True: asFeatures.append(u'64\u2011bit\u2011Guest');
309 if oEntry.fChipsetIoMmu is True: asFeatures.append(u'I/O\u2011MMU');
310 sFeatures = u' '.join(asFeatures) if len(asFeatures) > 0 else u'';
311
312 # Collection applicable actions.
313 aoActions = [
314 WuiTmLink('Details', WuiAdmin.ksScriptName,
315 { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestBoxDetails,
316 TestBoxData.ksParam_idTestBox: oEntry.idTestBox,
317 WuiAdmin.ksParamEffectiveDate: self._tsEffectiveDate, } ),
318 ]
319
320 if isDbTimestampInfinity(oEntry.tsExpire):
321 aoActions += [
322 WuiTmLink('Edit', WuiAdmin.ksScriptName,
323 { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestBoxEdit,
324 TestBoxData.ksParam_idTestBox: oEntry.idTestBox, } ),
325 WuiTmLink('Remove', WuiAdmin.ksScriptName,
326 { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestBoxRemovePost,
327 TestBoxData.ksParam_idTestBox: oEntry.idTestBox },
328 sConfirm = 'Are you sure that you want to remove %s (%s)?' % (oEntry.sName, oEntry.ip) ),
329 ]
330
331 if oEntry.sOs not in [ 'win', 'os2', ] and oEntry.ip is not None:
332 aoActions.append(WuiLinkBase('ssh', 'ssh://vbox@%s' % (oEntry.ip,),));
333
334 return [ self._getCheckBoxColumn(iEntry, oEntry.idTestBox),
335 [ WuiSpanText('tmspan-name', oEntry.sName), WuiRawHtml('<br>'), '%s' % (oEntry.ip,),],
336 aoLom,
337 [
338 '' if oEntry.fEnabled else 'disabled / ',
339 oState,
340 WuiRawHtml('<br>'),
341 oSeen,
342 ],
343 oEntry.enmPendingCmd,
344 oComment,
345 WuiSvnLink(oEntry.iTestBoxScriptRev),
346 oEntry.formatPythonVersion(),
347 aoGroups,
348 aoOs,
349 oCpu,
350 sFeatures,
351 oEntry.cCpus if oEntry.cCpus is not None else 'N/A',
352 utils.formatNumberNbsp(oEntry.cMbMemory) + u'\u00a0MB' if oEntry.cMbMemory is not None else 'N/A',
353 utils.formatNumberNbsp(oEntry.cMbScratch) + u'\u00a0MB' if oEntry.cMbScratch is not None else 'N/A',
354 aoActions,
355 ];
356
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