Skip to content

Commit 4767de9

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.
1 parent 7d627fc commit 4767de9

7 files changed

Lines changed: 87 additions & 5 deletions

File tree

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
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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,24 @@ NetworkYKUSHPowerPort
298298
A :any:`NetworkYKUSHPowerPort` describes a `YKUSHPowerPort`_ available on a
299299
remote computer.
300300

301+
AMTPowerPort
302+
++++++++++++
303+
A :any:`AMTPowerPort` describes a AMT port accessible via `amtctrl`.
304+
305+
.. code-block:: yaml
306+
307+
AMTPowerPort:
308+
host: 'amt-hostname'
309+
password: 'secret-password'
310+
311+
Arguments:
312+
- host (str): hostname or ip the AMT interface of the PC is reachable
313+
- password (str): password to use for AMT login
314+
- timeout (int): timeout to use when polling the resource
315+
316+
Used by:
317+
- `AMTPowerDriver`_
318+
301319
USBPowerPort
302320
++++++++++++
303321
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
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

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -744,8 +744,7 @@ def power(self):
744744
target = self._get_target(place)
745745
from ..resource.power import NetworkPowerPort, PDUDaemonPort
746746
from ..resource.remote import NetworkUSBPowerPort, NetworkSiSPMPowerPort
747-
from ..resource import TasmotaPowerPort, NetworkYKUSHPowerPort
748-
747+
from ..resource import TasmotaPowerPort, NetworkYKUSHPowerPort, AMTPowerPort
749748
drv = None
750749
try:
751750
drv = target.get_driver("PowerProtocol", name=name)
@@ -765,6 +764,8 @@ def power(self):
765764
drv = self._get_driver_or_new(target, "TasmotaPowerDriver", name=name)
766765
elif isinstance(resource, NetworkYKUSHPowerPort):
767766
drv = self._get_driver_or_new(target, "YKUSHPowerDriver", name=name)
767+
elif isinstance(resource, AMTPowerPort):
768+
drv = self._get_driver_or_new(target, "AMTPowerDriver", name=name)
768769
if drv:
769770
break
770771

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

labgrid/resource/power.py

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