harness: machines: define custom buildall targets for armv7/armv8 machines
[barrelfish] / tools / harness / machines / eth.py
1 ##########################################################################
2 # Copyright (c) 2009-2016 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, Universitaetstr 6, CH-8092 Zurich. Attn: Systems Group.
8 ##########################################################################
9
10 import os, getpass, subprocess, socket, pty
11 import debug, eth_machinedata
12 from machines import Machine, MachineLockedError, MachineFactory,\
13     MachineOperations
14
15 from subprocess_timeout import wait_or_terminate
16
17 TFTP_PATH='/home/netos/tftpboot'
18 TOOLS_PATH='/home/netos/tools/bin'
19 RACKBOOT=os.path.join(TOOLS_PATH, 'rackboot.sh')
20 RACKPOWER=os.path.join(TOOLS_PATH, 'rackpower')
21
22 class ETHBaseMachine(Machine):
23     _machines = None
24
25     def __init__(self, options,
26                  operations,
27                  serial_binary='serial_pc16550d',
28                  **kwargs):
29
30         super(ETHBaseMachine, self).__init__(options, operations,
31                                              serial_binary=serial_binary,
32                                              **kwargs)
33
34     def get_perfcount_type(self):
35         return self._perfcount_type
36
37 class ETHBaseMachineOperations(MachineOperations):
38
39     def __init__(self, machine):
40         super(ETHBaseMachineOperations, self).__init__(machine)
41         self.lockprocess = None
42         self.masterfd = None
43
44     def _get_console_status(self):
45         raise NotImplementedError
46
47     def lock(self):
48         """Use conserver to lock the machine."""
49
50         # find out current status of console
51         cstate = self._get_console_status()
52         # check that nobody else has it open for writing
53         myuser = getpass.getuser()
54         parts = cstate.strip().split(':')
55         conname, child, contype, details, users, state = parts[:6]
56         if users:
57             for userinfo in users.split(','):
58                 mode, username, host, port = userinfo.split('@')[:4]
59                 if 'w' in mode and username != myuser:
60                     raise MachineLockedError # Machine is not free
61
62         # run a console in the background to 'hold' the lock and read output
63         debug.verbose('starting "console %s"' % self._machine.get_machine_name())
64         # run on a PTY to work around terminal mangling code in console
65         (self.masterfd, slavefd) = pty.openpty()
66         self.lockprocess = subprocess.Popen(["console", self._machine.get_machine_name()],
67                                             close_fds=True,
68                                             stdout=slavefd, stdin=slavefd)
69         os.close(slavefd)
70         # XXX: open in binary mode with no buffering
71         # otherwise select.select() may block when there is data in the buffer
72         self.console_out = os.fdopen(self.masterfd, 'rb', 0)
73
74     def unlock(self):
75         if self.lockprocess is None:
76             return # noop
77         debug.verbose('quitting console process (%d)' % self.lockprocess.pid)
78         # os.kill(self.lockprocess.pid, signal.SIGTERM)
79         os.write(self.masterfd, "\x05c.")
80         wait_or_terminate(self.lockprocess)
81         self.lockprocess = None
82         self.masterfd = None
83
84     # this expects a pexpect object for `consolectrl`
85     def force_write(self, consolectrl):
86         try:
87             consolectrl.send('\x05cf')
88         except:
89             print "Unable to force write control through consolectrl, trying masterfd"
90             os.write(self.masterfd, "\x05cf")
91
92     def get_output(self):
93         return self.console_out
94
95
96 class ETHMachine(ETHBaseMachine):
97     _machines = eth_machinedata.machines
98
99     def __init__(self, options, **kwargs):
100         super(ETHMachine, self).__init__(options, ETHMachineOperations(self), **kwargs)
101
102     def get_buildall_target(self):
103         if 'buildall_target' in self._machines[self.name]:
104             return self._machines[self.name]['buildall_target']
105         return self.get_bootarch().upper() + "_Full"
106
107     def get_xphi_ncores(self):
108         if 'xphi_ncores' in self._machines[self.name] :
109             return self._machines[self.name]['xphi_ncores']
110         else :
111             return 0
112
113     def get_xphi_ncards(self):
114         if 'xphi_ncards' in self._machines[self.name] :
115             return self._machines[self.name]['xphi_ncards']
116         else :
117             return 0
118
119     def get_xphi_ram_gb(self):
120         if 'xphi_ram_gb' in self._machines[self.name] :
121             return self._machines[self.name]['xphi_ram_gb']
122         else :
123             return 0
124
125     def get_xphi_tickrate(self):
126         if 'xphi_tickrate' in self._machines[self.name] :
127             return self._machines[self.name]['xphi_tickrate']
128         else :
129             return 0
130
131     def get_hostname(self):
132         return self.get_machine_name() + '.in.barrelfish.org'
133
134     def get_ip(self):
135         return socket.gethostbyname(self.get_hostname())
136
137 class ETHMachineOperations(ETHBaseMachineOperations):
138
139     def __init__(self, machine):
140         super(ETHMachineOperations, self).__init__(machine)
141
142     def get_tftp_dir(self):
143         user = getpass.getuser()
144         return os.path.join(TFTP_PATH, user, self._machine.name + "_harness")
145
146     def get_tftp_subdir(self):
147         user = getpass.getuser()
148         return os.path.join(user, self._machine.name + "_harness")
149
150     def _write_menu_lst(self, data, path):
151         debug.verbose('writing %s' % path)
152         debug.debug(data)
153         with open(path, 'w') as f:
154             f.write(data)
155
156
157     def _get_menu_lst_name(self):
158         if self._machine.get_bootarch() == "armv8":
159             return "hagfish.cfg"
160         else:
161             return "menu.lst"
162
163     def _set_menu_lst(self, relpath):
164         ip_menu_name = os.path.join(TFTP_PATH, self._get_menu_lst_name() + "." + self._machine.get_ip())
165         debug.verbose('relinking %s to %s' % (ip_menu_name, relpath))
166         os.remove(ip_menu_name)
167         os.symlink(relpath, ip_menu_name)
168
169     def set_bootmodules(self, modules):
170         fullpath = os.path.join(self.get_tftp_dir(), self._get_menu_lst_name())
171         relpath = os.path.relpath(fullpath, TFTP_PATH)
172         tftppath = '/' + os.path.relpath(self.get_tftp_dir(), TFTP_PATH)
173         self._write_menu_lst(modules.get_menu_data(tftppath), fullpath)
174         self._set_menu_lst(relpath)
175
176     def _get_console_status(self):
177         debug.verbose('executing "console -i %s" to check state' %
178                       self._machine.get_machine_name())
179         proc = subprocess.Popen(["console", "-i", self._machine.get_machine_name()],
180                                 stdout=subprocess.PIPE)
181         line = proc.communicate()[0]
182         assert(proc.returncode == 0)
183         return line
184
185     def __rackboot(self, args):
186         debug.checkcmd([RACKBOOT] + args + [self._machine.get_machine_name()])
187
188     def setup(self):
189         if self._machine.get_bootarch() == "armv8":
190             self.__rackboot(["-b", "-H", "-n"])
191         else:
192             self.__rackboot(["-b", "-n"])
193
194     def __rackpower(self, arg):
195         try:
196             debug.checkcmd([RACKPOWER, arg, self._machine.get_machine_name()])
197         except subprocess.CalledProcessError:
198             debug.warning("rackpower %s %s failed" %
199                           (arg, self._machine.get_machine_name()))
200
201     def reboot(self):
202         self.__rackpower('-r')
203
204     def shutdown(self):
205         self.__rackpower('-d')
206
207
208 for n in sorted(ETHMachine._machines.keys()):
209     class TmpMachine(ETHMachine):
210         name = n
211     MachineFactory.addMachine(n, TmpMachine, **ETHMachine._machines[n])