blob: f30408f734b933d6d4a604399cfa45927b0c749d [file] [log] [blame] [edit]
# Copyright 2020 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from flask import Flask, jsonify, abort, request, make_response
from google.cloud import ndb
from marshmallow import Schema, fields
app = Flask(__name__)
client = ndb.Client()
class ApDevice(ndb.Model):
""" Model class to create AP entities to write to Datastore """
hostname = ndb.StringProperty()
lock_status = ndb.BooleanProperty(indexed=True)
lock_status_updated = ndb.DateTimeProperty(auto_now=True)
locked_by = ndb.StringProperty()
router_name = ndb.StringProperty()
lab_label = ndb.StringProperty()
ap_label = ndb.StringProperty()
class DataSerializer(Schema):
class Meta:
# The serialized output has all the items defined in fields param
fields = (
"id", "hostname", "lock_status", "lock_status_updated",
"locked_by", "router_name", "lab_label", "ap_label"
)
ndb_serialize = DataSerializer()
multi_ndb_serialize = DataSerializer(many=True)
@app.route('/')
def greeting():
return "Welcome to Chaos AP Page"
@app.route('/devices/new', methods=['POST'])
def post():
"""
Create a new AP in the datastore.
Requires a hostname, lab_label, ap_label, and router_name.
curl -i -H "Content-Type: application/json" -X POST -d \
'{"hostname":"<HOSTNAME>"}' http://localhost:8080/devices/new
@returns string if success, error 400 if fails.
"""
if not request.json:
abort(400, "Not a JSON request")
required_keys = ('hostname', 'router_name', 'ap_label', 'lab_label')
for _key in required_keys:
if _key in request.json and type(request.json[_key]) != str:
return make_response(jsonify({'error': f'{_key} value not a str'}))
with client.context():
hostname = request.json['hostname']
new_device = ApDevice(
id=hostname,
hostname=hostname,
lock_status=False)
new_device.router_name = request.json['router_name']
new_device.ap_label = request.json['ap_label']
new_device.lab_label = request.json['lab_label']
new_device.put()
return jsonify(result=True, id=hostname)
@app.route('/devices/<hostname>', methods=['PUT', 'GET'])
def edit_ap_device(hostname):
"""Get and/or edit an AP.
curl -i -H "Content-Type: application/json" <URL>/devices/<HOSTNAME>
curl -i -H "Content-Type: application/json" -X PUT -d '{"ap_label":"<AP_LABEL>"}' \
<URL>/devices/<HOSTNAME>
"""
with client.context():
device_key = ndb.Key(ApDevice, hostname)
device = device_key.get()
if request.method == 'GET':
return jsonify(ndb_serialize.dump(device))
if request.method == 'PUT':
if 'router_name' in request.json:
device.router_name = request.json['router_name']
if 'ap_label' in request.json:
device.ap_label = request.json['ap_label']
if 'lab_label' in request.json:
device.lab_label = request.json['lab_label']
device.put()
return jsonify(result=True, id=hostname, ap_label=device.ap_label,
lab_label=device.lab_label, router_name=device.router_name)
@app.route('/devices/lock', methods=['PUT'])
def lock_devices():
"""
Locks devices given either a list of hostnames or a hostname string.
curl -i -H "Content-Type: application/json" -X PUT \
-d '{"hostname":"<HOSTNAME>", "locked_by":"<LDAP>"}' \
<URL>/devices/lock
@returns Hosts locked message if success, error 400 if fails.
"""
if not request.json:
abort(400, "Not a JSON request")
with client.context():
hostname = request.json['hostname']
if type(hostname) is list:
hostnames = hostname
device_keys = [ndb.Key(ApDevice, x) for x in hostnames]
devices = ndb.get_multi(device_keys)
for d in devices:
d.lock_status = True
d.locked_by = request.json['locked_by']
ndb.put_multi(devices)
return jsonify(result=True, id=hostnames)
elif 'hostname' in request.json and type(request.json['hostname']) != str:
return make_response(jsonify({'error': 'Hostname not string'}), 400)
elif 'locked_by' in request.json and type(request.json['locked_by']) != str:
return make_response(jsonify({'error': 'locked_by name not string'}), 400)
else:
hostname = request.json['hostname']
device_key = ndb.Key(ApDevice, hostname)
device = device_key.get()
device.lock_status = True
device.locked_by = request.json['locked_by']
device.put()
return jsonify(result=True, id=hostname)
@app.route('/devices/unlock', methods=['PUT'])
def unlock_devices():
"""
Unlocks devices given either a list of hostnames or a hostname string.
curl -i -H "Content-Type: application/json" -X PUT \
-d '{"hostname":"<HOSTNAME>"}' <URL>/devices/unlock
@returns Hosts unlocked message if success, error 400 if fails.
"""
if not request.json:
return make_response(jsonify({'error': 'Need json dict.'}), 400)
with client.context():
hostname = request.json['hostname']
if type(hostname) is list:
hostnames = hostname
device_keys = [ndb.Key(ApDevice, x) for x in hostnames]
devices = ndb.get_multi(device_keys)
for d in devices:
d.lock_status = False
d.locked_by = None
ndb.put_multi(devices)
return jsonify(result=True, id=hostnames)
elif 'hostname' in request.json and type(request.json['hostname']) != str:
return make_response(jsonify({'error': 'Hostname not string'}), 400)
else:
device_key = ndb.Key(ApDevice, hostname)
device = device_key.get()
device.lock_status = False
device.locked_by = None
device.put()
return jsonify(result=True, id=hostname)
@app.route('/devices/delete', methods=['PUT'])
def delete():
"""Deletes AP in datastore given a hostname.
curl -i -H "Content-Type: application/json" -X PUT \
-d '{"hostname":"<HOSTNAME>"}' <URL>/devices/delete
@returns Hosts deleted message if success.
"""
with client.context():
hostname = request.json['hostname']
device_key = ndb.Key(ApDevice, hostname)
device_key.delete()
return "%s deleted.\n" % hostname
# Querying devices.
@app.route('/devices/', methods=['GET'])
def get_devices():
"""Get list of devices in datastore.
curl -i -H "Content-Type: application/json" <URL>/devices/
@returns list of devices
"""
with client.context():
devices_list = ApDevice.query().fetch()
return jsonify(multi_ndb_serialize.dump(devices_list))
@app.route('/devices/<hostname>', methods=['GET'])
def show_device(hostname):
"""Show one device with all fields.
curl -i -H "Content-Type: application/json" <URL>/devices/<HOSTNAME>
@returns python dict of device
"""
with client.context():
device_key = ndb.Key(ApDevice, hostname)
device = device_key.get()
return jsonify(ndb_serialize.dump(device))
@app.route('/unlocked_devices/', methods=['GET'])
def get_unlocked_devices():
"""Get list of unlocked devices in datastore.
curl -i -H "Content-Type: application/json" <URL>/unlocked_devices/
@returns list of devices
"""
with client.context():
devices_list = ApDevice.query(ApDevice.lock_status == False).fetch()
return jsonify(multi_ndb_serialize.dump(devices_list))
@app.route('/devices/location', methods=['PUT'])
def filter_by_location():
"""Get list of unlocked devices according to filter.
curl -i -H "Content-Type: application/json" -X PUT \
-d '{"ap_label":<AP_LABEL>, "lab_label":<LAB_LABEL>}' <URL>/location
@returns list of devices
"""
if not request.json:
return make_response(jsonify({'error': 'Need json dict.'}), 400)
with client.context():
devices_list = ApDevice.query(ApDevice.ap_label == request.json["ap_label"],
ApDevice.lab_label == request.json["lab_label"],
ApDevice.lock_status == False).fetch()
return jsonify(multi_ndb_serialize.dump(devices_list))
@app.errorhandler(404)
def not_found(error):
return make_response(jsonify({'error': 'Not found'}), 404)
if __name__ == '__main__':
app.run(host='127.0.0.1', port=8080, debug=True)