Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1#!/usr/bin/env python3
3import flask
4import json
5import os
6from pdu import PDU, PDUPort, PDUState
7import traceback
10app = flask.Flask(__name__)
13@app.before_first_request
14def populate_static_pdus():
15 statics = json.loads(os.environ.get('PDUGATEWAY_STATIC_PDUS', '[]'))
16 for entry in statics:
17 if not all([k in entry for k in ['model', 'pdu_name', 'config']]):
18 raise ValueError("static entry is missing one of 'model', 'pdu_name', or 'config'")
19 PDU.register(model_name=entry.get('model'),
20 pdu_name=entry.get('pdu_name'),
21 config=entry.get('config'))
24def get_pdu_or_fail(pdu_name):
25 pdu = PDU.get_by_name(pdu_name)
26 if pdu is None:
27 raise ValueError(f"Unknown pdu name '{pdu_name}'")
28 return pdu
31def has_no_empty_params(rule):
32 defaults = rule.defaults if rule.defaults is not None else ()
33 arguments = rule.arguments if rule.arguments is not None else ()
34 return len(defaults) >= len(arguments)
37@app.errorhandler(ValueError)
38def handle_valueError_exception(error):
39 traceback.print_exc()
40 response = flask.jsonify({"error": str(error)})
41 response.status_code = 400
42 return response
45@app.route("/")
46def site_map():
47 links = []
48 for rule in app.url_map.iter_rules():
49 # Filter out rules we can't navigate to in a browser
50 # and rules that require parameters
51 if "GET" in rule.methods and has_no_empty_params(rule):
52 url = flask.url_for(rule.endpoint, **(rule.defaults or {}))
53 links.append(url)
55 return {"routes": links}
58@app.route('/pdu_gateway')
59def pdu_gateway():
60 return {
61 "version": 1,
62 "supported_pdus": list(PDU.supported_pdus().keys()),
63 }
66@app.route('/v1/pdus', methods=['GET'])
67def pdus_list():
68 return {
69 "pdus": list(PDU.registered_pdus().keys())
70 }
73@app.route('/v1/pdus', methods=['DELETE'])
74def delete_all_pdus():
75 PDU.full_reset()
76 return ("", 200)
79@app.route('/v1/pdus', methods=['POST'])
80def register_pdu():
81 params = flask.request.json
83 if params is None:
84 raise ValueError("The request is not JSON-based")
86 PDU.register(model_name=params.get('model'),
87 pdu_name=params.get('pdu_name'),
88 config=params.get('config'))
89 return ("", 200)
92# TODO: Allow deleting a single PDU
95@app.route('/v1/pdus/<pdu_name>/ports')
96def list_ports(pdu_name):
97 pdu = get_pdu_or_fail(pdu_name)
99 ports = {}
100 for port in pdu.ports:
101 ports[port.port_id] = {
102 "state": port.state.name
103 }
104 if port.label is not None:
105 ports[port.port_id]["label"] = port.label
107 return ports
110@app.route('/v1/pdus/<pdu_name>/ports/<port_id>/state', methods=['GET'])
111def get_port_state(pdu_name, port_id):
112 pdu = get_pdu_or_fail(pdu_name)
113 return PDUPort(pdu, port_id).state.name + "\n"
116@app.route('/v1/pdus/<pdu_name>/ports/<port_id>/state', methods=['POST'])
117def set_port_state(pdu_name, port_id):
118 pdu = get_pdu_or_fail(pdu_name)
120 params = flask.request.json
121 if params is None:
122 raise ValueError("The request is not JSON-based")
124 if 'state' not in params:
125 return ("The request is missing the state that needs to be set", 400)
127 state = getattr(PDUState, params['state'], None)
128 if state is None or not state.is_valid_action:
129 valid_choices = [s.name for s in PDUState.valid_actions()]
130 return (f"The state '{params['state']}' is invalid: Should be in {valid_choices}\n", 400)
132 port = PDUPort(pdu, port_id)
133 port.set(state)
135 return port.state.name + "\n"
138# TODO: Add a way to label the ports in the PDU itself
140if __name__ == '__main__': # pragma: nocover
141 app.run()