Skip to content

Commit adfacf8

Browse files
committed
add AMTPowerPort and AMTPowerDriver to control AMT powered devices
This currently uses amtctrl as the backend as it is simpler then importing the library of the same tool. If more flexibility is needed the python amt library could be used. Signed-off-by: Tobias Binkowski <[email protected]>
1 parent cea524d commit adfacf8

File tree

7 files changed

+87
-5
lines changed

7 files changed

+87
-5
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
13+
Recommends: openssh-client, microcom, socat, sshfs, rsync, bash-completion, python3-amt
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

+18
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,24 @@ NetworkYKUSHPowerPort
310310
A :any:`NetworkYKUSHPowerPort` describes a `YKUSHPowerPort`_ available on a
311311
remote computer.
312312

313+
AMTPowerPort
314+
++++++++++++
315+
A :any:`AMTPowerPort` describes a AMT port accessible via `amtctrl`.
316+
317+
.. code-block:: yaml
318+
319+
AMTPowerPort:
320+
host: 'amt-hostname'
321+
password: 'secret-password'
322+
323+
Arguments:
324+
- host (str): hostname or ip the AMT interface of the PC is reachable
325+
- password (str): password to use for AMT login
326+
- timeout (int): timeout to use when polling the resource
327+
328+
Used by:
329+
- `AMTPowerDriver`_
330+
313331
USBPowerPort
314332
++++++++++++
315333
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
18+
PDUDaemonDriver, AMTPowerDriver
1919
from .usbloader import MXSUSBDriver, IMXUSBDriver, BDIMXUSBDriver, RKUSBDriver, UUUDriver
2020
from .usbsdmuxdriver import USBSDMuxDriver
2121
from .usbsdwiredriver import USBSDWireDriver

labgrid/driver/powerdriver.py

+48
Original file line numberDiff line numberDiff line change
@@ -440,3 +440,51 @@ def cycle(self):
440440
@Driver.check_active
441441
def get(self):
442442
raise NotImplementedError("pdudaemon does not support retrieving the port's state")
443+
444+
445+
@target_factory.reg_driver
446+
@attr.s(eq=False)
447+
class AMTPowerDriver(Driver, PowerResetMixin, PowerProtocol):
448+
"""AMTPowerDriver - Driver using an Intel AMT PowerPort"""
449+
bindings = {"port": "AMTPowerPort", }
450+
delay = attr.ib(default=5.0, validator=attr.validators.instance_of(float))
451+
452+
def __attrs_post_init__(self):
453+
super().__attrs_post_init__()
454+
455+
def _amt_power(self, cmd):
456+
runstr = f"amtctrl -p {self.port.host} {cmd}"
457+
p = subprocess.Popen(runstr.split(' '), text=True,
458+
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
459+
stdin=subprocess.PIPE)
460+
p.stdin.write(self.port.password)
461+
p.stdin.close()
462+
p.wait(self.port.timeout)
463+
out = p.stdout.read() + p.stderr.read()
464+
assert p.returncode == 0, "Failed to execute AMT command {cmd}, ret: {p.returncode}, {out}"
465+
return out.strip()
466+
467+
@Driver.check_active
468+
@step()
469+
def on(self):
470+
self._amt_power("on")
471+
472+
@Driver.check_active
473+
@step()
474+
def off(self):
475+
self._amt_power("off")
476+
477+
@Driver.check_active
478+
@step()
479+
def cycle(self):
480+
self._amt_power("reboot")
481+
482+
@Driver.check_active
483+
def get(self):
484+
power = self._amt_power("status")
485+
if power == "on":
486+
return True
487+
elif power == "off":
488+
return False
489+
else:
490+
raise ExecutionError(f"got unexpected power status {power}")

labgrid/remote/client.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -867,8 +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
871-
870+
from ..resource import TasmotaPowerPort, NetworkYKUSHPowerPort, AMTPowerPort
872871
drv = None
873872
try:
874873
drv = target.get_driver("PowerProtocol", name=name)
@@ -888,6 +887,8 @@ def power(self):
888887
drv = self._get_driver_or_new(target, "TasmotaPowerDriver", name=name)
889888
elif isinstance(resource, NetworkYKUSHPowerPort):
890889
drv = self._get_driver_or_new(target, "YKUSHPowerDriver", name=name)
890+
elif isinstance(resource, AMTPowerPort):
891+
drv = self._get_driver_or_new(target, "AMTPowerDriver", name=name)
891892
if drv:
892893
break
893894

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
8+
from .power import NetworkPowerPort, PDUDaemonPort, AMTPowerPort
99
from .remote import RemotePlace
1010
from .udev import (
1111
AlteraUSBBlaster,

labgrid/resource/power.py

+15
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,18 @@ class PDUDaemonPort(Resource):
3333
host = attr.ib(validator=attr.validators.instance_of(str))
3434
pdu = attr.ib(validator=attr.validators.instance_of(str))
3535
index = attr.ib(validator=attr.validators.instance_of(int), converter=int)
36+
37+
38+
@target_factory.reg_resource
39+
@attr.s(eq=False)
40+
class AMTPowerPort(Resource):
41+
"""The AMTPowerPort describes an Intel AMT power controllable PC with BMC
42+
43+
Args:
44+
host (str): hostname or ip the AMT interface of the PC is reachable
45+
password (str): password to use for AMT login
46+
timeout (int): timeout to use when polling the resource
47+
"""
48+
host = attr.ib(validator=attr.validators.instance_of(str))
49+
password = attr.ib(validator=attr.validators.instance_of(str))
50+
timeout = attr.ib(default=30, validator=attr.validators.instance_of(int))

0 commit comments

Comments
 (0)