123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279 |
- #
- # Copyright (c) 2017-2018 Lionel Hercot <lhercot@cisco.com>
- # All rights reserved.
- #
- import requests
- import sys
- import json
- from requests.packages.urllib3.exceptions import InsecureRequestWarning
- from flask import Flask
- from flask import abort
- from flask_restful import Api
- from flask_restful import Resource
- from flask_restful import reqparse
- from flask import send_file
- from pathlib import Path
- from io import StringIO
- from io import BytesIO
- from ipaddress import IPv6Address
- import os
- import traceback
- import CLEUCreds
- from cleu.config import Config as C
- from PIL import Image, ImageDraw
- app = Flask(__name__)
- api = Api(app)
- # Disable SSL warnings
- requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
- @app.after_request
- def after_request(response):
- response.headers.add("Access-Control-Allow-Origin", "*")
- response.headers.add("Access-Control-Allow-Headers", "Content-Type,Authorization")
- response.headers.add("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE")
- return response
- cmxBaseUrl = C.CMX
- cmxAuth = (CLEUCreds.CMX_USERNAME, CLEUCreds.CMX_PASSWORD)
- PORT = 8002
- imgDir = "maps/"
- markerDir = "markers/"
- markerFiles = os.listdir(markerDir)
- markers = {}
- for filename in markerFiles:
- name = filename.split(".")[0]
- markers[name] = Image.open(markerDir + filename).convert("RGBA")
- markers[name].thumbnail((100, 100), Image.ANTIALIAS)
- # markerW, markerH = marker.size
- apiUri = {
- "mac": "/api/location/v2/clients?macAddress=",
- "ip": "/api/location/v2/clients?ipAddress=",
- "map": "/api/config/v1/maps/imagesource/",
- "ssid": "/api/location/v2/clients/?include=metadata&pageSize=1000&page=",
- "count": "/api/location/v2/clients/count",
- "floors": "/api/config/v1/maps",
- "tag": "/api/location/v1/tags/",
- }
- def serve_pil_image(pil_img):
- img_io = BytesIO()
- pil_img.save(img_io, "JPEG", quality=100)
- img_io.seek(0)
- return send_file(img_io, mimetype="image/jpeg")
- def getMap(imageName):
- imgFile = Path(imgDir + imageName)
- if not imgFile.is_file():
- respImg = requests.get(cmxBaseUrl + apiUri["map"] + imageName, auth=cmxAuth, verify=False, stream=True)
- if respImg.status_code == 200:
- with open(str(imgFile), "wb") as f:
- for chunk in respImg:
- f.write(chunk)
- def getAllFloorMaps():
- response = requests.get(cmxBaseUrl + apiUri["floors"], auth=cmxAuth, verify=False)
- floorList = {}
- if response and response != "":
- try:
- dataDict = json.loads(response.text)
- if "campuses" in dataDict:
- for campus in dataDict["campuses"]:
- if "buildingList" in campus:
- for building in campus["buildingList"]:
- for floor in building["floorList"]:
- floorId = floor["aesUid"]
- imageName = floor["image"]["imageName"]
- floorList[floorId] = imageName
- getMap(imageName)
- except:
- print("Unexpected error" + str(sys.exc_info()[0]), file=sys.stderr)
- raise
- return floorList
- class CMX(Resource):
- def get(self):
- parser = reqparse.RequestParser()
- parser.add_argument("ip", help="IP address of the endpoint")
- parser.add_argument("ipv6", help="IPv6 address of the endpoint")
- parser.add_argument("mac", help="MAC address of the endpoint")
- parser.add_argument("marker", help="Marker used to display the location of the endpoint", default="marker")
- parser.add_argument("size", help="Size of the image returned")
- parser.add_argument("tag", help="Asset tag MAC address")
- args = parser.parse_args()
- response = ""
- if args.get("ip"):
- clientIp = args.get("ip")
- response = requests.get(cmxBaseUrl + apiUri["ip"] + clientIp, auth=cmxAuth, verify=False)
- elif args.get("tag"):
- clientMac = args.get("tag")
- response = requests.get(cmxBaseUrl + apiUri["tag"] + clientMac, auth=cmxAuth, verify=False)
- elif args.get("mac"):
- clientMac = args.get("mac")
- response = requests.get(cmxBaseUrl + apiUri["mac"] + clientMac, auth=cmxAuth, verify=False)
- elif args.get("ipv6"):
- clientIp = IPv6Address(args.get("ipv6")).exploded
- response = requests.get(cmxBaseUrl + apiUri["ip"] + clientIp, auth=cmxAuth, verify=False)
- markerName = args.get("marker")
- marker = markers["marker"]
- if markerName in markers:
- marker = markers[markerName]
- markerW, markerH = marker.size
- if response and response != "":
- try:
- dataDict = json.loads(response.text)
- result = None
- if args.get("tag") and dataDict and "mapInfo" in dataDict:
- result = dataDict
- elif len(dataDict) > 0 and "mapInfo" in dataDict[0]:
- result = dataDict[0]
- if result is not None:
- imageName = result["mapInfo"]["image"]["imageName"]
- mapLength = result["mapInfo"]["floorDimension"]["length"]
- mapWidth = result["mapInfo"]["floorDimension"]["width"]
- imageLength = result["mapInfo"]["image"]["height"]
- imageWidth = result["mapInfo"]["image"]["width"]
- coordX = result["mapCoordinate"]["x"]
- coordY = result["mapCoordinate"]["y"]
- positionX = (imageWidth / mapWidth) * coordX
- positionY = (imageLength / mapLength) * coordY
- getMap(imageName)
- im = Image.open(str(imgDir + imageName))
- positionX = positionX - markerW / 2
- positionY = positionY - markerH
- offset = (int(positionX), int(positionY))
- im.paste(marker, offset, marker)
- if args.get("size"):
- # print('SIZE', file=sys.stderr)
- size = args.get("size")
- im.thumbnail((int(size), int(size)), Image.ANTIALIAS)
- return serve_pil_image(im)
- else:
- abort(404, "Requested element not found")
- except Exception as inst:
- print("Unexpected error with request= {} | error : {}".format(response.text, inst), file=sys.stderr)
- return {"response": str(response.text), "error": str(inst)}, 500
- return abort(404, "Missing parameter ip, ipv6 or mac. Other possible parameters are: marker (" + ", ".join(markers.keys()) + ")")
- class CMX_SSID(Resource):
- def get(self):
- parser = reqparse.RequestParser()
- parser.add_argument("ssid", help="SSID used by the clients")
- parser.add_argument("floor", help="Floor used by the clients")
- parser.add_argument("marker", help="Marker used to display the location of the endpoint", default="marker")
- args = parser.parse_args()
- response = ""
- if args.get("ssid"):
- ssid = args.get("ssid")
- countResp = requests.get(cmxBaseUrl + apiUri["count"], auth=cmxAuth, verify=False)
- try:
- dataDict = json.loads(countResp.text)
- if "count" in dataDict:
- count = dataDict["count"]
- maxPageId = (count // 1000) + 1
- print("Count: {} MaxPage: {}".format(count, maxPageId), file=sys.stderr)
- userList = {}
- floorList = getAllFloorMaps()
- for pageId in range(1, maxPageId):
- print("Page: {} MaxPage: {}".format(pageId, maxPageId), file=sys.stderr)
- response = requests.get(cmxBaseUrl + apiUri["ssid"] + str(pageId), auth=cmxAuth, verify=False)
- if response and response.text != "":
- try:
- userDict = json.loads(response.text)
- for user in userDict:
- if user["ssId"] == ssid:
- floorName = user["mapInfo"]["floorRefId"]
- if floorName in userList:
- userList[floorName].append(user)
- else:
- userList[floorName] = [user]
- except:
- print(
- "Unexpected error with page request= " + response.text + " | error : " + str(sys.exc_info()[0]),
- file=sys.stderr,
- )
- return {"response": str(response.text), "error": str(inst)}, 500
- if args.get("floor"):
- floor = args.get("floor")
- if floor in userList:
- markerName = args.get("marker")
- marker = ""
- if markerName in markers:
- marker = markers[markerName]
- else:
- marker = markers["marker"]
- markerW, markerH = marker.size
- imageName = floorList[floorName]
- getMap()
- im = Image.open(str(imgDir + imageName))
- for data in userList[floor]:
- mapInfo = data["mapInfo"]
- mapLength = mapInfo["floorDimension"]["length"]
- mapWidth = mapInfo["floorDimension"]["width"]
- imageLength = mapInfo["image"]["height"]
- imageWidth = mapInfo["image"]["width"]
- coordX = data["mapCoordinate"]["x"]
- coordY = data["mapCoordinate"]["y"]
- positionX = (imageWidth / mapWidth) * coordX
- positionY = (imageLength / mapLength) * coordY
- positionX = positionX - markerW / 2
- positionY = positionY - markerH
- offset = (int(positionX), int(positionY))
- im.paste(marker, offset, marker)
- return serve_pil_image(im)
- elif floor in floorList:
- imageName = floorList[floor]
- getMap(imageName)
- im = Image.open(str(imgDir + imageName))
- return serve_pil_image(im)
- else:
- abort(404)
- else:
- return list(floorList.keys())
- except Exception as inst:
- print("Unexpected error with request= {} | error : {}".format(countResp, inst), file=sys.stderr)
- return {"response": str(countResp), "error": str(inst)}, 500
- class sync(Resource):
- def get(self):
- return getAllFloorMaps()
- class home(Resource):
- def get(self):
- return {}
- api.add_resource(home, "/")
- api.add_resource(CMX, "/api/v0.1/cmx")
- api.add_resource(CMX_SSID, "/api/v0.1/ssid")
- api.add_resource(sync, "/api/v0.1/sync")
- if __name__ == "__main__":
- app.run(host=C.WSGI_SERVER, debug=True, port=PORT, threaded=True)
|