Skip to content
This repository has been archived by the owner on Aug 13, 2019. It is now read-only.

Commit

Permalink
Added host performance information
Browse files Browse the repository at this point in the history
  • Loading branch information
Andy Southgate committed Apr 16, 2008
1 parent b095abf commit f344e48
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 40 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ PLUGINS_BASE += XSFeatureChangeTimeout.py
PLUGINS_BASE += XSFeatureClaimSR.py
PLUGINS_BASE += XSFeatureCrashDumpSR.py
PLUGINS_BASE += XSFeatureDNS.py
PLUGINS_BASE += XSFeatureHostInfo.py
PLUGINS_BASE += XSFeatureInstallLicence.py
PLUGINS_BASE += XSFeatureInterface.py
PLUGINS_BASE += XSFeatureKeyboard.py
Expand Down
8 changes: 4 additions & 4 deletions XSConsoleDialogueBases.py
Original file line number Diff line number Diff line change
Expand Up @@ -620,8 +620,8 @@ def UpdateFieldsINITIAL(self):
elapsedStr = TimeUtils.DurationString(durationSecs)

except Exception, e:
progressStr = Lang(e) # FIXME: Change to 'Unavailable'
elapsedStr = Lang(e)
progressStr = Lang('<Unavailable>')
elapsedStr = Lang('<Unavailable>')

pane.AddWrappedTextField(Lang('Time:', 16) + elapsedStr)
pane.AddWrappedTextField(Lang('Progress: ', 16) + progressStr)
Expand All @@ -646,8 +646,8 @@ def UpdateFieldsCANCEL(self):
elapsedStr = TimeUtils.DurationString(durationSecs)

except Exception, e:
progressStr = Lang(e) # FIXME: Change to 'Unavailable'
elapsedStr = Lang(e)
progressStr = Lang('<Unavailable>')
elapsedStr = Lang('<Unavailable>')

pane.AddWrappedTextField(Lang('Time:', 16) + elapsedStr)
pane.AddWrappedTextField(Lang('Progress: ', 16) + progressStr)
Expand Down
77 changes: 58 additions & 19 deletions XSConsoleHotData.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,25 +51,42 @@ def __getattr__(self, inName):
retVal.refs.append(None)
return retVal

def __iter__(self):
self.iterKeys = HotData.Inst().GetData(self.name, {}, self.refs).keys()
return self

# This method will hide fields called 'next' in the xapi database. If any appear, __iter__ will need to
# return a new object type and this method will need to be moved into that
def next(self):
if len(self.iterKeys) <= 0:
raise StopIteration
retVal = HotAccessor(self.name[:], self.refs[:]) # [:] copies the array
retVal.refs[-1] = self.iterKeys.pop(0)
return retVal

def __getitem__(self, inParam):
# These are square brackets selecting a particular item from a dict using its OpaqueRef
retVal = HotAccessor(self.name[:], self.refs[:])
if not isinstance(inParam, HotOpaqueRef):
raise Exception('Use of HotAccessor[param] requires param of type HotOpaqueRef, but got '+type(inParam))
retVal.refs[-1] = inParam
return retVal

def __call__(self, inParam = None):
# These are the brackets on the end of the statement, with optional default value.
# That makes it a request to fetch the data
if isinstance(inParam, HotOpaqueRef):
# Add a reference, selecting e.g. a key selecting a particular item from a dictionary
self.refs[-1] = inParam
retVal = self # Return this object for further operations
else:
# These are the brackets on the end of the statement, with optional default value.
# That makes it a request to fetch the data
retVal = HotData.Inst().GetData(self.name, inParam, self.refs)
return retVal
raise Exception('Use [] to pass HotOpaqueRefs to HotAccessors')
return HotData.Inst().GetData(self.name, inParam, self.refs)

def OpaqueRef(self):
return self.refs[-1]

def __str__(self):
return ",".join(zip(self.name, self.refs))
return str(self.__dict__)

def __repr__(self):
return __str__(self)
return str(self.__dict__)

class HotData:
instance = None
Expand Down Expand Up @@ -110,9 +127,6 @@ def GetData(self, inNames, inDefault, inRefs):
itemRef = self.data # Start at the top level

for i, name in enumerate(inNames):
if name is '__repr__':
raise Exception('HotData.' + '.'.join(inNames[:-1]) + ' must end with ()')

dataValue = itemRef.get(name, None)
fetcher = self.fetchers.get(name, None)
if fetcher is None:
Expand Down Expand Up @@ -147,8 +161,9 @@ def InitialiseFetchers(self):
self.AddFetcher('guest_vm', self.FetchGuestVM, 5)
self.AddFetcher('guest_vm_derived', self.FetchGuestVMDerived, 5)
self.AddFetcher('host', self.FetchHost, 5)
self.AddFetcher('host_CPUs', self.FetchHostCPUs, 5)
self.AddFetcher('local_host', self.FetchLocalHost, 5)
self.AddFetcher('metrics', self.FetchVMMetrics, 5)
self.AddFetcher('metrics', self.FetchMetrics, 5)
self.AddFetcher('vm', self.FetchVM, 5)

def FetchVMGuestMetrics(self, inOpaqueRef):
Expand All @@ -166,6 +181,23 @@ def FetchGuestVM(self, inOpaqueRef):
retVal[key] = value
return retVal

def FetchHostCPUs(self, inOpaqueRef):
def LocalConverter(inCPU):
return HotData.ConvertOpaqueRefs(inCPU,
host='host'
)

if inOpaqueRef is not None:
cpu = self.Session().xenapi.host_cpu.get_record(inOpaqueRef.OpaqueRef())
retVal = LocalConverter(cpu)
else:
cpus = self.Session().xenapi.host_cpu.get_all_records()
retVal = {}
for key, cpu in cpus.iteritems():
cpu = LocalConverter(cpu)
retVal[HotOpaqueRef(key, 'cpu')] = cpu
return retVal

def FetchGuestVMDerived(self, inOpaqueRef):
retVal = {}
halted = 0
Expand Down Expand Up @@ -196,6 +228,8 @@ def FetchLocalHost(self, inOpaqueRef):
raise Exception("Request for local host must not be passed an OpaqueRef")
thisHost = self.Session().xenapi.session.get_this_host(self.Session()._session)
retVal = self.FetchHost(HotOpaqueRef(thisHost, 'host'))
# Add an easy way to get the local host OpaqueRef
retVal['opaque_ref'] = HotOpaqueRef(thisHost, 'host')
return retVal

def FetchHost(self, inOpaqueRef):
Expand All @@ -204,7 +238,7 @@ def LocalConverter(inHost):
crash_dump_sr = 'sr',
consoles = 'console',
host_CPUs = 'cpu',
metrics = 'host.metrics',
metrics = 'host::metrics',
PBDs = 'pbd',
PIFs='pif',
resident_VMs = 'vm',
Expand All @@ -223,11 +257,16 @@ def LocalConverter(inHost):
host = LocalConverter(host)
retVal[HotOpaqueRef(key, 'host')] = host
return retVal

def FetchVMMetrics(self, inOpaqueRef):
def FetchMetrics(self, inOpaqueRef):
if inOpaqueRef is None:
raise Exception("Request for VM metrics requires an OpaqueRef")
retVal = self.Session().xenapi.VM_metrics.get_record(inOpaqueRef.OpaqueRef())
if inOpaqueRef.Type() == 'vm::metrics':
retVal = self.Session().xenapi.VM_metrics.get_record(inOpaqueRef.OpaqueRef())
elif inOpaqueRef.Type() == 'host::metrics':
retVal = self.Session().xenapi.host_metrics.get_record(inOpaqueRef.OpaqueRef())
else:
raise Exception("Unknown metrics type '"+inOpaqueRef.Type()+"'")
return retVal

def FetchVM(self, inOpaqueRef):
Expand All @@ -237,7 +276,7 @@ def LocalConverter(inVM):
consoles='console',
current_operations = 'task',
guest_metrics='guest_metrics',
metrics='metrics',
metrics='vm::metrics',
PIFs='pif',
resident_on='host',
suspend_VDI='vdi',
Expand Down
59 changes: 59 additions & 0 deletions plugins-base/XSFeatureHostInfo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Copyright (c) Citrix Systems 2008. All rights reserved.
# xsconsole is proprietary software.
#
# Xen, the Xen logo, XenCenter, XenMotion are trademarks or registered
# trademarks of Citrix Systems, Inc., in the United States and other
# countries.

if __name__ == "__main__":
raise Exception("This script is a plugin for xsconsole and cannot run independently")

from XSConsoleStandard import *

class XSFeatureHostInfo:
@classmethod
def StatusUpdateHandler(cls, inPane):
inPane.AddTitleField("Host Performance Information")

host = HotAccessor().local_host
numCPUs = len(host.host_CPUs({}))
try:
cpuUsage = sum( [cpu.utilisation() for cpu in host.host_CPUs] ) / numCPUs # Allow divide-by-zero to throw
cpuUsage = max(0.0, min(1.0, cpuUsage))
cpuUsageStr = "%d%% of %d CPUs" % (int(cpuUsage * 100), numCPUs)
except Exception, e:
cpuUsageStr = Lang('<Unavailable>')

try:
totalMemory = float(host.metrics.memory_total(0))
freeMemory = float(host.metrics.memory_free(0))
memoryUsage = (totalMemory - freeMemory) / totalMemory # Allow divide-by-zero to throw
memoryUsage = max(0.0, min(1.0, memoryUsage))
memoryUsageStr = "%d%% of %s" % (int(memoryUsage * 100), SizeUtils.MemorySizeString(totalMemory))
except Exception, e:
memoryUsageStr = Lang('<Unavailable>')

inPane.AddStatusField(Lang("CPU Usage", 16), cpuUsageStr)
inPane.AddStatusField(Lang("Memory Usage", 16), memoryUsageStr)

inPane.AddKeyHelpField( { Lang("<Enter>") : Lang("Control This Host") } )

@classmethod
def ActivateHandler(cls):
pass

def Register(self):
Importer.RegisterNamedPlugIn(
self,
'HOST_INFO', # Key of this plugin for replacement, etc.
{
'menuname' : 'MENU_VM',
'menupriority' : 200,
'menutext' : Lang('Host Performance Information') ,
'activatehandler' : XSFeatureHostInfo.ActivateHandler,
'statusupdatehandler' : XSFeatureHostInfo.StatusUpdateHandler
}
)

# Register this plugin when module is imported
XSFeatureHostInfo().Register()
7 changes: 5 additions & 2 deletions plugins-base/XSFeatureVMCommon.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class VMUtils:
'RESUME' : Lang("Resume"),
'SHUTDOWN' : Lang("Shut Down"),
'START' : Lang("Start"),
'STARTONTHISHOST' : Lang("Start On This Host"),
'SUSPEND' : Lang("Suspend")
}
@classmethod
Expand All @@ -37,6 +38,9 @@ def AsyncOperation(cls, inVMHandle, inOperation):
task = Task.New(lambda x: x.xenapi.Async.VM.clean_shutdown(inVMHandle.OpaqueRef()))
elif inOperation == 'START':
task = Task.New(lambda x: x.xenapi.Async.VM.start(inVMHandle.OpaqueRef(), False, True))
elif inOperation == 'STARTONTHISHOST':
hostRef = HotAccessor().local_host.opaque_ref()
task = Task.New(lambda x: x.xenapi.Async.VM.start_on(inVMHandle.OpaqueRef(), hostRef.OpaqueRef(), False, True))
elif inOperation == 'SUSPEND':
task = Task.New(lambda x: x.xenapi.Async.VM.suspend(inVMHandle.OpaqueRef()))
else:
Expand All @@ -50,7 +54,6 @@ def DoOperation(cls, inVMHandle, inOperation):

while task.IsPending():
time.sleep(1)


@classmethod
def OperationName(cls, inOperation):
Expand All @@ -72,7 +75,7 @@ def __init__(self, inVMHandle):
elif powerState.startswith('suspended'):
choiceList = [ 'RESUME', 'FORCESHUTDOWN' ]
elif powerState.startswith('halted'):
choiceList = [ 'START' ]
choiceList = [ 'STARTONTHISHOST' ]
else:
choiceList = [ 'NONE' ]

Expand Down
6 changes: 3 additions & 3 deletions plugins-base/XSFeatureVMInfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def NoVMStatusUpdateHandler(cls, inPane):

@classmethod
def InfoStatusUpdateHandler(cls, inPane, inHandle):
vm = HotAccessor().vm(inHandle)
vm = HotAccessor().vm[inHandle]
if vm is None:
inPane.AddWrappedTextField(Lang("This virtual machine is no longer present"))
else:
Expand Down Expand Up @@ -98,7 +98,7 @@ def MenuRegenerator(cls, inList, inMenu):
retVal = copy.copy(inMenu)
retVal.RemoveChoices()
# inList is a list of HotOpaqueRef objects
vmList = [ HotAccessor().vm(x) for x in inList ]
vmList = [ HotAccessor().vm[x] for x in inList ]
# Sort list by VM name
vmList.sort(lambda x,y: cmp(x.name_label(''), y.name_label('')))

Expand Down Expand Up @@ -144,7 +144,7 @@ def Register(self):
{
'menuname' : 'MENU_ALLVM', # Name of the menu this item leads to when selected
'menutext' : Lang('All VMs'),
'menupriority' : 200,
'menupriority' : 300,
'menuregenerator' : XSFeatureVMInfo.AllMenuRegenerator,
'activatehandler' : XSFeatureVMInfo.AllActivateHandler,
'statusupdatehandler' : XSFeatureVMInfo.AllStatusUpdateHandler
Expand Down
28 changes: 16 additions & 12 deletions xsconsole.e4p
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Project SYSTEM "Project-4.0.dtd">
<!-- eric4 project file for project xsconsole -->
<!-- Saved: 2008-04-14, 18:33:28 -->
<!-- Saved: 2008-04-16, 18:49:24 -->
<!-- Copyright (C) 2008 , -->
<Project version="4.0">
<ProgLanguage mixed="0">Python</ProgLanguage>
Expand Down Expand Up @@ -227,6 +227,10 @@
<Source>
<Name>XSConsoleTask.py</Name>
</Source>
<Source>
<Dir>plugins-base</Dir>
<Name>XSFeatureHostInfo.py</Name>
</Source>
</Sources>
<Forms>
</Forms>
Expand Down Expand Up @@ -275,22 +279,22 @@
<bool>True</bool>
</value>
<key>
<string>enableFormat</string>
<string>enableExceptions</string>
</key>
<value>
<bool>True</bool>
</value>
<key>
<string>enableBasic</string>
<string>enableFormat</string>
</key>
<value>
<bool>True</bool>
</value>
<key>
<string>enableDesign</string>
<string>enableRPython</string>
</key>
<value>
<bool>True</bool>
<bool>False</bool>
</value>
<key>
<string>dialogReport</string>
Expand All @@ -305,7 +309,7 @@
<bool>True</bool>
</value>
<key>
<string>enableVariables</string>
<string>htmlReport</string>
</key>
<value>
<bool>True</bool>
Expand All @@ -317,22 +321,22 @@
<unicode>/home/asouthgate/build.hg/myrepos/xsconsole.hg/pylint.conf</unicode>
</value>
<key>
<string>htmlReport</string>
<string>enableDesign</string>
</key>
<value>
<bool>True</bool>
</value>
<key>
<string>enableSimilarities</string>
<string>enableNewstyle</string>
</key>
<value>
<bool>True</bool>
</value>
<key>
<string>enableRPython</string>
<string>enableBasic</string>
</key>
<value>
<bool>False</bool>
<bool>True</bool>
</value>
<key>
<string>txtReport</string>
Expand All @@ -341,7 +345,7 @@
<bool>False</bool>
</value>
<key>
<string>enableExceptions</string>
<string>enableVariables</string>
</key>
<value>
<bool>True</bool>
Expand All @@ -353,7 +357,7 @@
<bool>True</bool>
</value>
<key>
<string>enableNewstyle</string>
<string>enableSimilarities</string>
</key>
<value>
<bool>True</bool>
Expand Down

0 comments on commit f344e48

Please sign in to comment.