Skip to content

Commit 8676c44

Browse files
committed
add IPMIPowerPort and IPMIPowerDriver to control IPMI powered devices
this currently uses ipmi-power tool from freeipmi-tools
1 parent 967e9b8 commit 8676c44

File tree

7 files changed

+94
-4
lines changed

7 files changed

+94
-4
lines changed

debian/control

Lines changed: 1 addition & 1 deletion
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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,26 @@ Arguments:
316316
Used by:
317317
- `AMTPowerDriver`_
318318

319+
IPMIPowerPort
320+
++++++++++++
321+
A :any:`IPMIPowerPort` describes a IPMI port accessible via `ipmi-power`.
322+
323+
.. code-block:: yaml
324+
325+
IPMIPowerPort:
326+
host: 'ipmi-hostname'
327+
username: 'admin'
328+
password: 'secret-password'
329+
330+
Arguments:
331+
- host (str): hostname or ip the IPMI interface of the PC is reachable
332+
- username (str): username to use for IPMI login
333+
- password (str): password to use for IPMI login
334+
- timeout (int): timeout to use when polling the resource
335+
336+
Used by:
337+
- `IPMIPowerDriver`_
338+
319339
USBPowerPort
320340
++++++++++++
321341
A :any:`USBPowerPort` describes a generic switchable USB hub as supported by

labgrid/driver/__init__.py

Lines changed: 1 addition & 1 deletion
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

Lines changed: 51 additions & 0 deletions
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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -853,7 +853,7 @@ def power(self):
853853
target = self._get_target(place)
854854
from ..resource.power import NetworkPowerPort, PDUDaemonPort
855855
from ..resource.remote import NetworkUSBPowerPort, NetworkSiSPMPowerPort
856-
from ..resource import TasmotaPowerPort, NetworkYKUSHPowerPort, AMTPowerPort
856+
from ..resource import TasmotaPowerPort, NetworkYKUSHPowerPort, AMTPowerPort, IPMIPowerPort
857857
drv = None
858858
try:
859859
drv = target.get_driver("PowerProtocol", name=name)
@@ -875,6 +875,8 @@ def power(self):
875875
drv = self._get_driver_or_new(target, "YKUSHPowerDriver", name=name)
876876
elif isinstance(resource, AMTPowerPort):
877877
drv = self._get_driver_or_new(target, "AMTPowerDriver", name=name)
878+
elif isinstance(resource, IPMIPowerPort):
879+
drv = self._get_driver_or_new(target, "IPMIPowerDriver", name=name)
878880
if drv:
879881
break
880882

labgrid/resource/__init__.py

Lines changed: 1 addition & 1 deletion
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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,20 @@ 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+
"""
64+
host = attr.ib(validator=attr.validators.instance_of(str))
65+
username = attr.ib(validator=attr.validators.instance_of(str))
66+
password = attr.ib(validator=attr.validators.instance_of(str))
67+
timeout = attr.ib(default=30, validator=attr.validators.instance_of(int))

0 commit comments

Comments
 (0)