Skip to content

Commit c69184d

Browse files
committed
add IPMIPowerPort and IPMIPowerDriver to control IPMI powered devices
this currently uses ipmi-power tool from freeipmi-tools Signed-off-by: Tobias Binkowski <[email protected]>
1 parent adfacf8 commit c69184d

File tree

7 files changed

+96
-4
lines changed

7 files changed

+96
-4
lines changed

debian/control

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Package: labgrid
1010
Architecture: any
1111
Pre-Depends: dpkg (>= 1.16.1), python3, ${misc:Pre-Depends}
1212
Depends: ${python3:Depends}, ${misc:Depends}, ${shlibs:Depends}
13-
Recommends: openssh-client, microcom, socat, sshfs, rsync, bash-completion, python3-amt
13+
Recommends: openssh-client, microcom, socat, sshfs, rsync, bash-completion, python3-amt, freeipmi-tools
1414
Description: embedded board control python library
1515
Labgrid is an embedded board control python library with a focus on testing,
1616
development and general automation. It includes a remote control layer to

doc/configuration.rst

+20
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,26 @@ Arguments:
328328
Used by:
329329
- `AMTPowerDriver`_
330330

331+
IPMIPowerPort
332+
++++++++++++
333+
A :any:`IPMIPowerPort` describes a IPMI port accessible via `ipmi-power`.
334+
335+
.. code-block:: yaml
336+
337+
IPMIPowerPort:
338+
host: 'ipmi-hostname'
339+
username: 'admin'
340+
password: 'secret-password'
341+
342+
Arguments:
343+
- host (str): hostname or ip the IPMI interface of the PC is reachable
344+
- username (str): username to use for IPMI login
345+
- password (str): password to use for IPMI login
346+
- timeout (int): timeout to use when polling the resource
347+
348+
Used by:
349+
- `IPMIPowerDriver`_
350+
331351
USBPowerPort
332352
++++++++++++
333353
A :any:`USBPowerPort` describes a generic switchable USB hub as supported by

labgrid/driver/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from .powerdriver import ManualPowerDriver, ExternalPowerDriver, \
1616
DigitalOutputPowerDriver, YKUSHPowerDriver, \
1717
USBPowerDriver, SiSPMPowerDriver, NetworkPowerDriver, \
18-
PDUDaemonDriver, AMTPowerDriver
18+
PDUDaemonDriver, AMTPowerDriver, IPMIPowerDriver
1919
from .usbloader import MXSUSBDriver, IMXUSBDriver, BDIMXUSBDriver, RKUSBDriver, UUUDriver
2020
from .usbsdmuxdriver import USBSDMuxDriver
2121
from .usbsdwiredriver import USBSDWireDriver

labgrid/driver/powerdriver.py

+51
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import shlex
2+
import subprocess
23
import time
34
import math
45
from importlib import import_module
@@ -488,3 +489,53 @@ def get(self):
488489
return False
489490
else:
490491
raise ExecutionError(f"got unexpected power status {power}")
492+
493+
494+
@target_factory.reg_driver
495+
@attr.s(eq=False)
496+
class IPMIPowerDriver(Driver, PowerResetMixin, PowerProtocol):
497+
"""IPMIPowerDriver - Driver using an IPMI PowerPort"""
498+
bindings = {"port": "IPMIPowerPort", }
499+
delay = attr.ib(default=5.0, validator=attr.validators.instance_of(float))
500+
501+
def _ipmi_power(self, cmd):
502+
runstr = f"ipmi-power -h {self.port.host} -u {self.port.username} "
503+
runstr += f"--session-timeout={self.port.timeout*1000} "
504+
runstr += f"-p {self.port.password} {cmd}"
505+
runstr = runstr.split(' ')
506+
if len(self.port.args) > 0:
507+
runstr.append(self.port.args)
508+
return subprocess.run(runstr, capture_output=True)
509+
510+
def _ipmi_cmd(self, cmd):
511+
ret = self._ipmi_power(cmd)
512+
stdout = ret.stdout.decode('utf-8').split(' ')
513+
assert self.port.host == stdout[0][:-1]
514+
assert "ok" == stdout[1][:-1]
515+
516+
@Driver.check_active
517+
@step()
518+
def on(self):
519+
self._ipmi_cmd('--on')
520+
521+
@Driver.check_active
522+
@step()
523+
def off(self):
524+
self._ipmi_cmd('--off')
525+
526+
@Driver.check_active
527+
@step()
528+
def cycle(self):
529+
self._ipmi_cmd('--cycle')
530+
531+
@Driver.check_active
532+
def get(self):
533+
ret = self._ipmi_power('--stat')
534+
stdout = ret.stdout.decode('utf-8').split(' ')
535+
assert self.port.host == stdout[0][:-1]
536+
power = stdout[1][:-1]
537+
if power == 'off':
538+
return False
539+
elif power == 'on':
540+
return True
541+
raise ExecutionError(f"got unexpected power status {power}")

labgrid/remote/client.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -867,7 +867,7 @@ def power(self):
867867
target = self._get_target(place)
868868
from ..resource.power import NetworkPowerPort, PDUDaemonPort
869869
from ..resource.remote import NetworkUSBPowerPort, NetworkSiSPMPowerPort
870-
from ..resource import TasmotaPowerPort, NetworkYKUSHPowerPort, AMTPowerPort
870+
from ..resource import TasmotaPowerPort, NetworkYKUSHPowerPort, AMTPowerPort, IPMIPowerPort
871871
drv = None
872872
try:
873873
drv = target.get_driver("PowerProtocol", name=name)
@@ -889,6 +889,8 @@ def power(self):
889889
drv = self._get_driver_or_new(target, "YKUSHPowerDriver", name=name)
890890
elif isinstance(resource, AMTPowerPort):
891891
drv = self._get_driver_or_new(target, "AMTPowerDriver", name=name)
892+
elif isinstance(resource, IPMIPowerPort):
893+
drv = self._get_driver_or_new(target, "IPMIPowerDriver", name=name)
892894
if drv:
893895
break
894896

labgrid/resource/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from .modbusrtu import ModbusRTU
66
from .networkservice import NetworkService
77
from .onewireport import OneWirePIO
8-
from .power import NetworkPowerPort, PDUDaemonPort, AMTPowerPort
8+
from .power import NetworkPowerPort, PDUDaemonPort, AMTPowerPort, IPMIPowerPort
99
from .remote import RemotePlace
1010
from .udev import (
1111
AlteraUSBBlaster,

labgrid/resource/power.py

+19
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,22 @@ class AMTPowerPort(Resource):
4848
host = attr.ib(validator=attr.validators.instance_of(str))
4949
password = attr.ib(validator=attr.validators.instance_of(str))
5050
timeout = attr.ib(default=30, validator=attr.validators.instance_of(int))
51+
52+
53+
@target_factory.reg_resource
54+
@attr.s(eq=False)
55+
class IPMIPowerPort(Resource):
56+
"""The IPMIPowerPrt describes an IPMI controllable PC with BMC
57+
58+
Args:
59+
host (str): hostname or ip the IPMI interface of the PC is reachable
60+
username (str): username to use for IPMI login
61+
password (str): password to use for IPMI login
62+
timeout (int): timeout to use when polling the resource
63+
args (str): extra args to prepend the command with
64+
"""
65+
host = attr.ib(validator=attr.validators.instance_of(str))
66+
username = attr.ib(validator=attr.validators.instance_of(str))
67+
password = attr.ib(validator=attr.validators.instance_of(str))
68+
timeout = attr.ib(default=30, validator=attr.validators.instance_of(int))
69+
args = attr.ib(default="", validator=attr.validators.instance_of(str))

0 commit comments

Comments
 (0)