1 ##########################################################################
2 # Copyright (c) 2013, 2014, University of Washington.
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 ##########################################################################
10 import sys, os, signal, time, getpass, subprocess, socket, pty
11 import debug, machines, uw_machinedata
12 from machines import Machine, MachineLockedError, MachineFactory
14 TFTP_PATH='/var/lib/tftpboot'
15 TOOLS_PATH='/usr/local/bin'
16 RACKBOOT=os.path.join(TOOLS_PATH, 'rackboot.sh')
17 RACKPOWER=os.path.join(TOOLS_PATH, 'rackpower')
19 class UWMachine(Machine):
20 _uw_machines = uw_machinedata.machines
23 'bigfish.cs.washington.edu': 'bigfish-e1k1.cs.washington.edu',
24 'swingout1.cs.washington.edu': 'swingout1-brcm1.cs.washington.edu',
25 'swingout5.cs.washington.edu': 'swingout5-brcm1.cs.washington.edu'
28 def __init__(self, options):
29 super(UWMachine, self).__init__(options)
30 self.lockprocess = None
33 def get_bootarch(self):
34 b = self._uw_machines[self.name]['bootarch']
35 assert(b in self.get_buildarchs())
38 def get_machine_name(self):
39 return self._uw_machines[self.name]['machine_name']
41 def get_buildarchs(self):
42 return self._uw_machines[self.name]['buildarchs']
45 return self._uw_machines[self.name]['ncores']
47 def get_cores_per_socket(self):
48 return self._uw_machines[self.name]['cores_per_socket']
50 def get_tickrate(self):
51 return self._uw_machines[self.name]['tickrate']
53 def get_perfcount_type(self):
54 return self._uw_machines[self.name]['perfcount_type']
56 def get_kernel_args(self):
57 return self._uw_machines[self.name].get('kernel_args')
59 def get_pci_args(self):
60 return self._uw_machines[self.name].get('pci_args')
62 def get_boot_timeout(self):
63 return self._uw_machines[self.name].get('boot_timeout')
65 def get_hostname(self):
66 return self.get_machine_name() + '.cs.washington.edu'
69 return socket.gethostbyname(self.host2mgmt[self.get_hostname()])
71 def get_tftp_dir(self):
72 user = getpass.getuser()
73 return os.path.join(TFTP_PATH, user, self.name + "_harness")
75 def _write_menu_lst(self, data, path):
76 debug.verbose('writing %s' % path)
82 def _set_menu_lst(self, relpath):
83 ip_menu_name = os.path.join(TFTP_PATH, "menu.lst." + self.get_ip())
84 debug.verbose('relinking %s to %s' % (ip_menu_name, relpath))
85 os.remove(ip_menu_name)
86 os.symlink(relpath, ip_menu_name)
88 def set_bootmodules(self, modules):
89 fullpath = os.path.join(self.get_tftp_dir(), 'menu.lst')
90 relpath = os.path.relpath(fullpath, TFTP_PATH)
91 tftppath = '/' + os.path.relpath(self.get_tftp_dir(), TFTP_PATH)
92 self._write_menu_lst(modules.get_menu_data(tftppath), fullpath)
93 self._set_menu_lst(relpath)
96 """Use conserver to lock the machine."""
98 # find out current status of console
99 debug.verbose('executing "console -i %s" to check state' %
100 self.get_machine_name())
101 proc = subprocess.Popen(["console", "-i", self.get_machine_name()],
102 stdout=subprocess.PIPE)
103 line = proc.communicate()[0]
104 assert(proc.returncode == 0)
106 # check that nobody else has it open for writing
107 myuser = getpass.getuser()
108 parts = line.strip().split(':')
109 conname, child, contype, details, users, state = parts[:6]
111 for userinfo in users.split(','):
112 mode, username, host, port = userinfo.split('@')[:4]
113 if 'w' in mode and username != myuser:
114 raise MachineLockedError # Machine is not free
116 # run a console in the background to 'hold' the lock and read output
117 debug.verbose('starting "console %s"' % self.get_machine_name())
118 # run on a PTY to work around terminal mangling code in console
119 (self.masterfd, slavefd) = pty.openpty()
120 self.lockprocess = subprocess.Popen(["console", self.get_machine_name()],
122 stdout=slavefd, stdin=slavefd)
124 # XXX: open in binary mode with no buffering
125 # otherwise select.select() may block when there is data in the buffer
126 self.console_out = os.fdopen(self.masterfd, 'rb', 0)
129 if self.lockprocess is None:
131 debug.verbose('quitting console process (%d)' % self.lockprocess.pid)
132 # os.kill(self.lockprocess.pid, signal.SIGTERM)
133 os.write(self.masterfd, "\x05c.")
134 self.lockprocess.wait()
135 self.lockprocess = None
138 def __rackboot(self, args):
139 debug.checkcmd([RACKBOOT] + args + [self.get_machine_name()])
142 self.__rackboot(["-b", "-n"])
144 def __rackpower(self, arg):
149 debug.checkcmd([RACKPOWER, arg, self.get_machine_name()])
150 except subprocess.CalledProcessError:
151 debug.warning("rackpower %s %s failed" %
152 (arg, self.get_machine_name()))
155 debug.verbose("retrying...")
161 self.__rackpower('-r')
164 self.__rackpower('-d')
166 def get_output(self):
167 return self.console_out
170 for n in sorted(UWMachine._uw_machines.keys()):
171 class TmpMachine(UWMachine):
173 MachineFactory.addMachine(n, TmpMachine, **UWMachine._uw_machines[n])