ComputeServerMaintenanceImpl.mesa
The Compute Server Statistics program running on the server that informs the controller of its current state.
Last Edited by: Bob Hagmann, November 8, 1985 8:06:44 am PST
Copyright © 1984 by Xerox Corporation. All rights reserved.
DIRECTORY
BasicTime,
Commander,
CommandTool,
ComputeServerControllerRpcControl,
ComputeServerInternal,
ComputeServerRpcControl,
ComputeServerControl,
ComputeServerServer,
ComputeServerStatistics,
FSBackdoor,
FSFileOps,
Idle,
IO,
LupineRuntime,
MessageWindow,
PrincOps,
Process,
ProcessBackdoor,
Rope,
RPC,
SummonerControllerControl,
SymTab,
SystemVersion,
UserCredentials,
UserProfile,
ViewerClasses,
ViewerIO,
ViewerOps,
VMSideDoor,
VMStatistics,
WatchStats;
ComputeServerMaintenanceImpl: CEDAR MONITOR
IMPORTS BasicTime, Commander, CommandTool, ComputeServerControllerRpcControl, ComputeServerInternal, ComputeServerRpcControl, ComputeServerServer, ComputeServerStatistics, FSBackdoor, FSFileOps, Idle, IO, LupineRuntime, MessageWindow, Process, ProcessBackdoor, Rope, RPC, SummonerControllerControl, SymTab, SystemVersion, UserCredentials, UserProfile, ViewerIO, ViewerOps, VMSideDoor, VMStatistics, WatchStats
EXPORTS ComputeServerControl, ComputeServerInternal, ComputeServerStatistics
SHARES ComputeServerServer
= BEGIN
Variable Declarations
ROPE: TYPE = Rope.ROPE;
TimeUpThisIncarnation: PUBLIC INT ← 0;
RequestesThisIncarnation: PUBLIC INT ← 0;
TimeBusyThisIncarnation: PUBLIC INT ← 0;
TotalTimeUp: PUBLIC INT ← 0;
TotalRequestes: PUBLIC INT ← 0;
TotalTimeBusy: PUBLIC INT ← 0 ;
watchStatsRecord: WatchStats.WatchStatsRecord;
LastRMReclamations: INT;
LastTime: BasicTime.GMT ;
serverMachineName: RPC.ShortROPE;
serverMachinePupAddress: RPC.ShortROPE;
wasUp: BOOL ← FALSE;
PackageEntry: TYPE = ComputeServerInternal.PackageEntry;
CmdEntryObject: TYPE = ComputeServerInternal.CmdEntryObject;
CmdEntry: TYPE = ComputeServerInternal.CmdEntry;
ServiceEnabled: PUBLIC BOOLFALSE;
IdleTransitionsEnabled: PUBLIC BOOL ← FALSE;
Server Startup and Shutdown
ServerOn: Commander.CommandProc = {
argv: CommandTool.ArgumentVector ← NIL;
controllerName, remoteCommandDirectory, localCommandDirectory: Rope.ROPENIL;
IF ServiceEnabled THEN RETURN ["Server already enabled"];
argv ← CommandTool.Parse[cmd ! CommandTool.Failed => { msg ← errorMsg; CONTINUE; }];
IF argv = NIL THEN RETURN[$Failure, msg];
IF argv.argc > 1 THEN controllerName ← argv[1];
IF argv.argc > 2 THEN remoteCommandDirectory ← argv[2];
IF argv.argc > 3 THEN localCommandDirectory ← argv[3];
SummonerControllerControl.StartUpController[controllerName];
msg ← StartUpServer[controllerName, remoteCommandDirectory, localCommandDirectory];
IF ~Rope.IsEmpty[msg] THEN SummonerControllerControl.ShutDownController[];
};
StartUpServer: PUBLIC PROC [controllerName: Rope.ROPENIL, remoteCommandDirectory: Rope.ROPENIL, localCommandDirectory: Rope.ROPENIL] RETURNS [msg: ROPENIL] = {
name, password: ROPE;
[name, password] ← UserCredentials.Get[];
msg ← ComputeServerInternal.InitCommands[remoteCommandDirectory, localCommandDirectory];
IF ~Rope.IsEmpty[msg] THEN RETURN;
ServiceEnabled ← TRUE;
TRUSTED {Process.Detach[FORK TickStatistics[]]; };
ComputeServerInternal.ControllerGVName ← IF Rope.IsEmpty[controllerName] THEN UserProfile.Token["Summoner.ControllerName"] ELSE controllerName;
IF ComputeServerInternal.ControllerGVName = NIL THEN ComputeServerInternal.ControllerGVName ← "PaloAlto.summoner" ;
ComputeServerRpcControl.ExportInterface[
interfaceName: [ type: "ComputeServer.summoner", instance: ComputeServerInternal.MyNetAddressRope, version: [1,1]],
user: name,
password: RPC.MakeKey[password]
! LupineRuntime.BindingError => CONTINUE;];
ComputeServerInternal.ControllerInterface ← ComputeServerControllerRpcControl.ImportNewInterface[
interfaceName: [ type: "ComputeServerController.summoner", instance: ComputeServerInternal.ControllerGVName, version: [1,1]]
! RPC.ImportFailed => {
SummonerControllerControl.ControllerCannnotBeImported[why];
CONTINUE;
};
];
IF ComputeServerInternal.ControllerInterface # NIL THEN {
SummonerControllerControl.NoticeControllerIsUp[];
TellControllerAboutExtraCommands[];
};
ComputeServerInternal.InitStats[ComputeServerInternal.myHostName, ComputeServerInternal.MyNetAddressRope];
TRUSTED {Process.Detach[FORK ComputeServerInternal.SummonerServerMonitor[]]; };
};
ServerOff: Commander.CommandProc = {
ShutDownServer[];
SummonerControllerControl.ShutDownController[];
};
ShutDownServer: PUBLIC PROC [] RETURNS [] = {
ComputeServerInternal.ControllerInterface ← NIL ;
ServiceEnabled ← FALSE;
ComputeServerInternal.PackageTable ← SymTab.Create[mod: 59, case: FALSE];
ComputeServerInternal.ConfigTable ← SymTab.Create[mod: 59, case: FALSE];
};
DoStartup: PROC = {
msg: ROPE;
SummonerControllerControl.StartUpController[ComputeServerInternal.ControllerGVName];
msg ← StartUpServer[ComputeServerInternal.ControllerGVName, ComputeServerInternal.RemoteCommandDir, ComputeServerInternal.LocalCommandDir];
IF ~Rope.IsEmpty[msg] THEN {
SummonerControllerControl.ShutDownController[];
MessageWindow.Append[message: Rope.Concat["Can't Start Summoner Server because ", msg], clearFirst: TRUE];
};
};
IdleTransition: Idle.IdleHandler = {
PROC [data: REF, reason: IdleReason]
IF IdleTransitionsEnabled THEN {
SELECT reason FROM
becomingBusy => {
ShutDownServer[];
SummonerControllerControl.ShutDownController[];
};
becomingIdle => {
TRUSTED {Process.Detach[FORK DoStartup[]];};
};
ENDCASE;
};
};
EnableAutoIdle: Commander.CommandProc = {
IdleTransitionsEnabled ← TRUE;
};
DisableAutoIdle: Commander.CommandProc = {
IdleTransitionsEnabled ← FALSE;
};
Issue Statistics
SendStats: PUBLIC PROC [] RETURNS [success, terminateService, newPackage: BOOLFALSE, queueingCommands: LIST OF Rope.ROPE] = {
May Raise RPC.CallFailed
controllerInterface: ComputeServerControllerRpcControl.InterfaceRecord;
numberCPUs: CARDINAL ← 1;
diskPartionSize: INT;
freePagesOnDisk: INT;
freeboard: INT;
oldestLRUFileDate: BasicTime.GMT;
freeProcesses: CARDINAL ← 0;
NowRMReclamations: INT;
ReclamationRate: REAL;
NowTime: BasicTime.GMT = BasicTime.Now[];
TimePeriod: REAL;
firstCall: BOOL;
[diskPartionSize, freePagesOnDisk, freeboard] ← FSBackdoor.VolumePages[];
Get values for freeGFI, freeMDS, freeVM, and CPULoad
watchStatsRecord ← WatchStats.GetWatchStats[];
NowRMReclamations ← VMStatistics.rmReclamations;
TimePeriod ← BasicTime.Period[LastTime, NowTime];
TimePeriod ← MAX[1.0, TimePeriod];
IF LastRMReclamations = 0 THEN ReclamationRate ← -1.0
ELSE ReclamationRate ← (NowRMReclamations - LastRMReclamations) / TimePeriod ;
LastRMReclamations ← NowRMReclamations;
oldestLRUFileDate ← FSFileOps.OldestLruDate[];
IF FSFileSpaceImpl.lru.first = NIL THEN oldestLRUFileDate ← BasicTime.nullGMT
ELSE oldestLRUFileDate ← FSFileSpaceImpl.lru.first.used;
freeProcesses ← ProcessBackdoor.CountFreeProcesses[];
CRITICAL SECTION BEGIN
TRUSTED {
pbsIndex: PrincOps.PsbIndex;
firstPBSIndex: PrincOps.PsbIndex ;
UNTIL PrincOpsUtils.Enter[@ProcessImpl.processLock] DO NULL ENDLOOP;
pbsIndex ← LOOPHOLE[ProcessImpl.rebirth, PrincOps.ConditionVariable].condition.tail;
firstPBSIndex ← pbsIndex;
IF pbsIndex # PrincOps.PsbNull THEN DO
freeProcesses ← freeProcesses + 1;
pbsIndex ← PrincOps.PDA.block[pbsIndex].link.next;
IF pbsIndex = firstPBSIndex OR pbsIndex = PrincOps.PsbNull THEN EXIT;
ENDLOOP;
PrincOpsUtils.Exit[@ProcessImpl.processLock];
};
CRITICAL SECTION END
firstCall ← ~wasUp AND ServiceEnabled;
controllerInterface ← ComputeServerInternal.ControllerInterface;
IF controllerInterface # NIL THEN [terminateService, newPackage, queueingCommands] ← controllerInterface.NewStats[serverMachineName: serverMachineName, serverMachinePupAddress: serverMachinePupAddress, serverUP: ServiceEnabled, firstCall: firstCall, machineType: SystemVersion.machineType, mainMemory: VMSideDoor.rmPages, numberCPUs: numberCPUs, diskPartionSize: diskPartionSize, freePagesOnDisk: freePagesOnDisk, freeboard: freeboard, freeGFI: watchStatsRecord.gfiFree, freeMDS: watchStatsRecord.mdsFree, freeVM: watchStatsRecord.vmFree, oldestLRUFileDate: oldestLRUFileDate, CPULoad: watchStatsRecord.cpuLoad, reclamationRate: ReclamationRate, freeProcesses: freeProcesses ];
success ← TRUE;
wasUp ← ServiceEnabled;
};
Server Monitor
SummonerServerMonitor: PUBLIC PROC [] RETURNS [] = {
delayToNextStat: INT ← 2;
WHILE ServiceEnabled DO
success: BOOLTRUE;
terminateService: BOOLFALSE;
newPackage: BOOLFALSE;
queueingCommands: LIST OF ROPE;
cycleCount: INT ← 0;
tryDifferentController: BOOLFALSE;
WHILE ~tryDifferentController AND ComputeServerInternal.ControllerInterface # NIL AND success AND NOT terminateService AND ServiceEnabled DO
Process.Pause[Process.SecondsToTicks[delayToNextStat]];
[success, terminateService, newPackage, queueingCommands] ← ComputeServerInternal.SendStats[
! RPC.CallFailed => {
SummonerControllerControl.ControllerCallFailed[why];
ComputeServerInternal.ControllerInterface ← NIL ;
newPackage ← FALSE;
queueingCommands ← NIL;
CONTINUE;
};
];
IF newPackage THEN {
ComputeServerInternal.getNewPackages[];
};
IF queueingCommands # NIL THEN newQueueingCommands[queueingCommands];
newPackage ← FALSE;
delayToNextStat ← 9;
IF (cycleCount ← cycleCount + 1) > 10 THEN {
cycleCount ← 0;
ComputeServerInternal.KillOldUnstartedServices[];
};
IF cycleCount = 1 OR cycleCount = 6 THEN {
controllerInterface: ComputeServerControllerRpcControl.InterfaceRecord ← ComputeServerInternal.ControllerInterface;
IF controllerInterface # NIL THEN {
[tryDifferentController: tryDifferentController] ← controllerInterface.GetSomeInfo[ ! RPC.CallFailed => {
SummonerControllerControl.ControllerCallFailed[why];
ComputeServerInternal.ControllerInterface ← NIL ;
newPackage ← FALSE;
queueingCommands ← NIL;
CONTINUE;
};
];
};
};
ENDLOOP;
Send final message that we are no longer a server
IF ~tryDifferentController AND ComputeServerInternal.ControllerInterface # NIL AND ~ServiceEnabled THEN [success, terminateService] ← ComputeServerInternal.SendStats[
! RPC.CallFailed => {
SummonerControllerControl.ControllerCallFailed[why];
ComputeServerInternal.ControllerInterface ← NIL ;
CONTINUE;
};
];
IF terminateService THEN ServiceEnabled ← FALSE;
IF ServiceEnabled AND (tryDifferentController OR ComputeServerInternal.ControllerInterface = NIL) THEN ComputeServerInternal.ControllerInterface ← ComputeServerControllerRpcControl.ImportNewInterface[
interfaceName: [ type: "ComputeServerController.summoner", instance: ComputeServerInternal.ControllerGVName, version: [1,1]]
! RPC.ImportFailed => {
SummonerControllerControl.ControllerCannnotBeImported[why];
CONTINUE;
};];
IF ComputeServerInternal.ControllerInterface # NIL THEN {
SummonerControllerControl.NoticeControllerIsUp[];
TellControllerAboutExtraCommands[];
};
ComputeServerInternal.InitStats[ComputeServerInternal.myHostName, ComputeServerInternal.MyNetAddressRope];
ComputeServerInternal.KillOldUnstartedServices[];
IF ComputeServerInternal.ControllerInterface = NIL THEN Process.Pause[Process.SecondsToTicks[7]];
ENDLOOP;
};
Queueing Maintence
newQueueingCommands: PROC [queueingCommands: LIST OF ROPE] = {
FOR q: LIST OF ROPE ← queueingCommands, q.rest UNTIL q = NIL DO
foundInCmdTable: BOOL;
cmdVal: REF ANY;
cmdEntry: CmdEntry;
[found: foundInCmdTable, val: cmdVal] ← SymTab.Fetch[x: ComputeServerInternal.CommandTable, key: q.first];
cmdEntry ← NARROW[cmdVal];
IF cmdEntry # NIL THEN {
foundPack: BOOL;
valPack: REF ANY;
packageEntry: PackageEntry;
cmdEntry.doQueueing ← TRUE;
[found: foundPack, val: valPack] ← SymTab.Fetch[x: ComputeServerInternal.PackageTable, key: cmdEntry.package];
IF ~foundPack THEN LOOP;
packageEntry ← NARROW[valPack, PackageEntry];
IF packageEntry.exclusive AND ComputeServerInternal.ActiveServices > 0 THEN LOOP;
IF packageEntry.nowActive < packageEntry.maxCountActive THEN {
controllerInterface: ComputeServerControllerRpcControl.InterfaceRecord;
controllerInterface ← ComputeServerInternal.ControllerInterface;
IF controllerInterface # NIL THEN {
controllerInterface.MightAcceptQueuedCommand[serverMachineAddress: ComputeServerInternal.MyNetAddressRope, commandName: q.first ! RPC.CallFailed => {
SummonerControllerControl.ControllerCallFailed[why];
ComputeServerInternal.ControllerInterface ← NIL ;
CONTINUE;
};
];
cmdEntry.okToQueuePosted ← TRUE;
};
};
};
ENDLOOP;
};
New Controller Processing
TellControllerAboutExtraCommands: PROC = {
procData: LIST OF ComputeServerServer.RegisteredProcHandle;
EachPairProc: SymTab.EachPairAction = {
loopList: LIST OF ComputeServerInternal.RegisteredProcHandle;
procData ← NARROW[val];
FOR loopList ← procData, loopList.rest UNTIL loopList = NIL DO
procHandle: ComputeServerInternal.RegisteredProcHandle ← loopList.first;
foundCmdTab: BOOL;
tempControllerInterface: ComputeServerControllerRpcControl.InterfaceRecord ← NIL;
[found: foundCmdTab] ← SymTab.Fetch[x: ComputeServerInternal.CommandTable, key: procHandle.service];
IF ~foundCmdTab AND (tempControllerInterface ← ComputeServerInternal.ControllerInterface) # NIL THEN {
tempControllerInterface.ExtraCommandAvailable[serverMachineName: ComputeServerInternal.MyNetAddressRope, commandName: procHandle.service, version: procHandle.version ! RPC.CallFailed => {
SummonerControllerControl.ControllerCallFailed[why];
ComputeServerInternal.ControllerInterface ← NIL ;
CONTINUE;
};
];
};
ENDLOOP;
};
[] ← SymTab.Pairs[x: ComputeServerServer.Registry, action: EachPairProc];
};
Startup Stats
InitStats: PUBLIC PROC [myName: RPC.ShortROPE, myAddress: RPC.ShortROPE] = {
serverMachineName ← myName ;
serverMachinePupAddress ← myAddress;
LastRMReclamations ← VMStatistics.rmReclamations;
LastTime ← BasicTime.Now[];
};
Server Stats
TickStatistics: PROC [] = {
lastStatTime: BasicTime.GMT ← BasicTime.Now[];
aSecond: Process.Ticks = Process.SecondsToTicks[1];
WHILE ServiceEnabled DO
nowTime: BasicTime.GMT ← BasicTime.Now[];
period: INT;
period ← BasicTime.Period[lastStatTime, nowTime];
IF period > 0 THEN {
TimeUpThisIncarnation ← TimeUpThisIncarnation + period;
TotalTimeUp ← TotalTimeUp + period;
IF ComputeServerInternal.ActiveServices > 0 THEN {
TimeBusyThisIncarnation ← TimeBusyThisIncarnation + period;
TotalTimeBusy ← TotalTimeBusy + period;
};
};
lastStatTime ← nowTime;
Process.Pause[aSecond];
ENDLOOP;
TimeUpThisIncarnation ← 0;
RequestesThisIncarnation ← 0;
TimeBusyThisIncarnation ← 0;
};
ServerStatistics: Commander.CommandProc = {
IF ServiceEnabled THEN {
percentBusy: REAL;
realTimeBusyThisIncarnation: REAL ← TimeBusyThisIncarnation;
realTimeUpThisIncarnation: REAL ← TimeUpThisIncarnation;
percentBusy ← (100.0 * realTimeBusyThisIncarnation) / MAX[1.0, realTimeUpThisIncarnation];
msg ← IO.PutFR["Seconds up %g, requests %g, time busy %g, percent busy %5.2f%%", IO.int[TimeUpThisIncarnation], IO.int[RequestesThisIncarnation], IO.int[TimeBusyThisIncarnation], IO.real[percentBusy] ]
}
ELSE msg ← "Server not enabled";
};
Server Event Typescript
StartServerEventTypescript: Commander.CommandProc = {
TRUSTED {Process.Detach[FORK ServerEventTypescript[]];};
};
ServerEventTypescript: PROC = {
ENABLE IO.Error => GOTO ViewerDestroyed;
currentViewer: ViewerClasses.Viewer ← NIL;
currentStream: IO.STREAMNIL;
currentEvent: REF READONLY ComputeServerStatistics.ServerEvent ← NIL;
currentViewer ← ViewerOps.CreateViewer[flavor: $Typescript, info: [iconic: FALSE, name: "Compute Server ""Server"" Event Log"]];
currentStream ← ViewerIO.CreateViewerStreams[name: "", viewer: currentViewer].out;
currentStream.PutF["Starting log at %g\n", IO.time[BasicTime.Now[]]];
DO
currentEvent ← ComputeServerStatistics.NextServerEvent[currentEvent];
SELECT currentEvent.type FROM
startService => {
currentStream.PutF["Start %g for %g at %g\n", IO.rope[currentEvent.command], IO.rope[currentEvent.remoteMachineName], IO.time[currentEvent.startTime]];
};
doneService => {
currentStream.PutF["End %g for %g at %g duration %g seconds\n", IO.rope[currentEvent.command], IO.rope[currentEvent.remoteMachineName], IO.time[currentEvent.endTime], IO.int[BasicTime.Period[currentEvent.startTime, currentEvent.endTime]]];
};
ENDCASE;
ENDLOOP;
EXITS
ViewerDestroyed => {};
};
Initialization
Commander.Register[key: "///Commands/SummonerServerOn", proc: ServerOn, doc: "SummonerServerOn {controllerName {remoteCommandDirectory {localCommandDirectory}}} Turn on use of the Compute Server Service for cluster controllerName using remoteCommandDirectory on a file server and localCommandDirectory on the workstation"];
Commander.Register[key: "///Commands/SummonerServerOff", proc: ServerOff, doc: "Turn off use of the Compute Server Service"];
Commander.Register[key: "///Commands/SummonerEnableAutoIdle", proc: EnableAutoIdle, doc: "Turn on automatic Summoner server toggle at Idle"];
Commander.Register[key: "///Commands/SummonerDisableAutoIdle", proc: DisableAutoIdle, doc: "Turn off automatic Summoner server toggle at Idle"];
Commander.Register[key: "///Commands/SummonerServerTypescript", proc: StartServerEventTypescript, doc: "Start typescript of server events"];
Commander.Register[key: "///Commands/SummonerServerStatistics", proc: ServerStatistics, doc: "Print statistics"];
[] ← Idle.RegisterIdleHandler[IdleTransition];
END.
Bob Hagmann May 3, 1985 8:24:08 am PDT
changes to: DIRECTORY, ComputeServerStatsImpl, SendStats
Bob Hagmann January 17, 1986 8:33:08 am PST
Added check for server already enabled in ServerOn
changes to: ServerOn