1########################################################################## 2# Copyright (c) 2009, ETH Zurich. 3# All rights reserved. 4# 5# This file is distributed under the terms in the attached LICENSE file. 6# If you do not find this file, copies can be found by writing to: 7# ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group. 8########################################################################## 9 10import sys, os, signal, time, getpass, subprocess, socket, pty 11import debug, machines, msrc_machinedata 12from machines import Machine, MachineLockedError, MachineFactory 13 14TFTP_PATH='/tftpboot' 15TOOLS_PATH='/home/netos/tools/bin' 16RACKBOOT=os.path.join(TOOLS_PATH, 'rackboot.sh') 17RACKPOWER=os.path.join(TOOLS_PATH, 'rackpower') 18 19class MSRCMachine(Machine): 20 _msrc_machines = msrc_machinedata.machines 21 22 def __init__(self, options): 23 super(MSRCMachine, self).__init__(options) 24 self.lockprocess = None 25 26 def get_bootarch(self): 27 b = self._msrc_machines[self.name]['bootarch'] 28 assert(b in self.get_buildarchs()) 29 return b 30 31 def get_machine_name(self): 32 return self._msrc_machines[self.name]['machine_name'] 33 34 def get_buildarchs(self): 35 return self._msrc_machines[self.name]['buildarchs'] 36 37 def get_ncores(self): 38 return self._msrc_machines[self.name]['ncores'] 39 40 def get_cores_per_socket(self): 41 return self._msrc_machines[self.name]['cores_per_socket'] 42 43 def get_tickrate(self): 44 return self._msrc_machines[self.name]['tickrate'] 45 46 def get_perfcount_type(self): 47 return self._msrc_machines[self.name]['perfcount_type'] 48 49 def get_hostname(self): 50 return self.get_machine_name() 51 52 def get_ip(self): 53 return socket.gethostbyname(self.get_hostname()) 54 55 def get_tftp_dir(self): 56 user = getpass.getuser() 57 return os.path.join(TFTP_PATH, user, self.name + "_harness") 58 59 def _write_menu_lst(self, data, path): 60 debug.verbose('writing %s' % path) 61 debug.debug(data) 62 f = open(path, 'w') 63 f.write(data) 64 f.close() 65 66 def _set_menu_lst(self, relpath): 67 ip_menu_name = os.path.join(TFTP_PATH, "menu.lst." + self.get_ip()) 68 debug.verbose('relinking %s to %s' % (ip_menu_name, relpath)) 69 os.remove(ip_menu_name) 70 os.symlink(relpath, ip_menu_name) 71 72 def set_bootmodules(self, modules): 73 fullpath = os.path.join(self.get_tftp_dir(), 'menu.lst') 74 relpath = os.path.relpath(fullpath, TFTP_PATH) 75 tftppath = '/' + os.path.relpath(self.get_tftp_dir(), TFTP_PATH) 76 self._write_menu_lst(modules.get_menu_data(tftppath), fullpath) 77 self._set_menu_lst(relpath) 78 79 def lock(self): 80 """Use conserver to lock the machine.""" 81 82 # find out current status of console 83 debug.verbose('executing "console -i %s" to check state' % 84 self.get_machine_name()) 85 proc = subprocess.Popen(["console", "-i", self.get_machine_name()], 86 stdout=subprocess.PIPE) 87 line = proc.communicate()[0] 88 assert(proc.returncode == 0) 89 90 # check that nobody else has it open for writing 91 myuser = getpass.getuser() 92 parts = line.strip().split(':') 93 conname, child, contype, details, users, state = parts[:6] 94 if users: 95 for userinfo in users.split(','): 96 mode, username, host, port = userinfo.split('@')[:4] 97 if 'w' in mode and username != myuser: 98 raise MachineLockedError # Machine is not free 99 100 # run a console in the background to 'hold' the lock and read output 101 debug.verbose('starting "console %s"' % self.get_machine_name()) 102 # run on a PTY to work around terminal mangling code in console 103 (masterfd, slavefd) = pty.openpty() 104 self.lockprocess = subprocess.Popen(["console", self.get_machine_name()], 105 close_fds=True, 106 stdout=slavefd, stdin=slavefd) 107 os.close(slavefd) 108 # XXX: open in binary mode with no buffering 109 # otherwise select.select() may block when there is data in the buffer 110 self.console_out = os.fdopen(masterfd, 'rb', 0) 111 112 def unlock(self): 113 if self.lockprocess is None: 114 return # noop 115 debug.verbose('terminating console process (%d)' % self.lockprocess.pid) 116 os.kill(self.lockprocess.pid, signal.SIGTERM) 117 self.lockprocess.wait() 118 self.lockprocess = None 119 120 def __rackboot(self, args): 121 debug.checkcmd([RACKBOOT] + args + [self.get_machine_name()]) 122 123 def setup(self): 124 self.__rackboot(["-b", "-n"]) 125 126 def __rackpower(self, arg): 127 try: 128 debug.checkcmd([RACKPOWER, arg, self.get_machine_name()]) 129 except subprocess.CalledProcessError: 130 debug.warning("rackpower %s %s failed" % 131 (arg, self.get_machine_name())) 132 133 def reboot(self): 134 self.__rackpower('-r') 135 136 def shutdown(self): 137 self.__rackpower('-d') 138 139 def get_output(self): 140 return self.console_out 141 142 143for n in sorted(MSRCMachine._msrc_machines.keys()): 144 class TmpMachine(MSRCMachine): 145 name = n 146 MachineFactory.addMachine(n, TmpMachine) 147