123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- #
- # 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
- 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 = 'http://10.100.253.139'
- cmxAuth = ('admin', 'B4rc4123!')
- 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/v1/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)}
- 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)}
- 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)}
- 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='10.100.253.13', debug=True, port=8002, threaded=True)
|