Skip to content

Commit b67bfc1

Browse files
authored
Merge pull request #1512 from moto-timo/digitalloggers_restapi
driver: digitalloggers_restapi: enable REST API
2 parents d4d8370 + 71e3604 commit b67bfc1

File tree

3 files changed

+80
-0
lines changed

3 files changed

+80
-0
lines changed

doc/configuration.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,14 @@ Currently available are:
163163
host argument must include the protocol, such as
164164
``http://192.168.0.3`` or ``http://admin:[email protected]``.
165165

166+
``digitalloggers_restapi``
167+
Controls *Digital Loggers PDUs* that use the REST API. Note that
168+
host argument must include the protocol, such as
169+
``http://192.168.0.3`` or ``https://admin:[email protected]``.
170+
By default, only authenticated users may access the REST API.
171+
HTTPS queries intentially ignore ssl certificate validation, since
172+
the as-shipped certificate is self-signed.
173+
166174
``eaton``
167175
Controls *Eaton ePDUs* via SNMP.
168176

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
'''
2+
Driver for Digital Loggers PDU that use the REST API.
3+
Tested with Ethernet Power Controller 7.
4+
5+
Based on https://www.digital-loggers.com/restapi.pdf
6+
7+
By default, only an authenticated user is allowed by REST API.
8+
9+
NetworkPowerPort:
10+
model: 'digitalloggers_restapi'
11+
host: 'http://admin:[email protected]'
12+
index: 0
13+
'''
14+
from urllib.parse import urlparse
15+
16+
import requests
17+
from requests.auth import HTTPDigestAuth
18+
from requests.packages import urllib3
19+
20+
21+
def extract_user_password_from_host(host):
22+
url = urlparse(host)
23+
if '@' in url.netloc:
24+
user=url.username
25+
password=url.password
26+
_host= f'{url.scheme}://{url.netloc.split("@")[1]}'
27+
else:
28+
user = None
29+
password = None
30+
_host= f'{url.scheme}://{url.netloc}'
31+
return user, password, _host
32+
33+
def power_set(host, port, index, value):
34+
# curl -u admin:1234 -v -X PUT -H "X-CSRF: x" --data 'value=true' --digest http://192.168.0.100/restapi/relay/outlets/=0/state/
35+
# curl -u admin:1234 -v -X PUT -H "X-CSRF: x" --data 'value=false' --digest http://192.168.0.100/restapi/relay/outlets/=0/state/
36+
# curl --insecure -u admin:1234 -v -X PUT -H "X-CSRF: x" --data 'value=true' --digest https://192.168.0.100/restapi/relay/outlets/=0/state/
37+
# curl --insecure -u admin:1234 -v -X PUT -H "X-CSRF: x" --data 'value=false' --digest https://192.168.0.100/restapi/relay/outlets/=0/state/
38+
assert port is None
39+
40+
index = int(index)
41+
value = 'true' if value else 'false'
42+
payload = {'value' : value }
43+
headers = {'X-CSRF': 'x', 'Accept': 'application/json'}
44+
user, password, url = extract_user_password_from_host(host)
45+
host = f'{url}/restapi/relay/outlets/={index}/state/'
46+
with urllib3.warnings.catch_warnings():
47+
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
48+
if user and password:
49+
r = requests.put(host, data=payload, auth=HTTPDigestAuth(user, password), headers=headers, verify=False)
50+
else:
51+
r = requests.put(host, data=payload, headers=headers, verify=False)
52+
r.raise_for_status()
53+
54+
def power_get(host, port, index):
55+
# curl -u admin:1234 -v -X GET -H "X-CSRF: x" --digest http://192.168.0.100/restapi/relay/outlets/=0/state/
56+
# curl --insecure -u admin:1234 -v -X GET -H "X-CSRF: x" --digest https://192.168.0.100/restapi/relay/outlets/=0/state/
57+
assert port is None
58+
59+
index = int(index)
60+
user, password, url = extract_user_password_from_host(host)
61+
headers = {'X-CSRF': 'x', 'Accept': 'application/json'}
62+
host = f'{url}/restapi/relay/outlets/={index}/state/'
63+
with urllib3.warnings.catch_warnings():
64+
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
65+
if user and password:
66+
r = requests.get(host, auth=HTTPDigestAuth(user, password), headers=headers, verify=False)
67+
else:
68+
r = requests.get(host, headers=headers, verify=False)
69+
r.raise_for_status()
70+
statuses = r.json()
71+
return statuses[0]

tests/test_powerdriver.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ def test_import_backends(self):
279279
import labgrid.driver.power.apc
280280
import labgrid.driver.power.digipower
281281
import labgrid.driver.power.digitalloggers_http
282+
import labgrid.driver.power.digitalloggers_restapi
282283
import labgrid.driver.power.eth008
283284
import labgrid.driver.power.gude
284285
import labgrid.driver.power.gude24

0 commit comments

Comments
 (0)