cmx.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. #
  2. # Copyright (c) 2017-2018 Lionel Hercot <lhercot@cisco.com>
  3. # All rights reserved.
  4. #
  5. import requests
  6. import sys
  7. import json
  8. from requests.packages.urllib3.exceptions import InsecureRequestWarning
  9. from flask import Flask
  10. from flask import abort
  11. from flask_restful import Api
  12. from flask_restful import Resource
  13. from flask_restful import reqparse
  14. from flask import send_file
  15. from pathlib import Path
  16. from io import StringIO
  17. from io import BytesIO
  18. from ipaddress import IPv6Address
  19. import os
  20. import traceback
  21. from PIL import Image, ImageDraw
  22. app = Flask(__name__)
  23. api = Api(app)
  24. # Disable SSL warnings
  25. requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
  26. @app.after_request
  27. def after_request(response):
  28. response.headers.add('Access-Control-Allow-Origin', '*')
  29. response.headers.add('Access-Control-Allow-Headers',
  30. 'Content-Type,Authorization')
  31. response.headers.add('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE')
  32. return response
  33. cmxBaseUrl = 'http://10.100.253.139'
  34. cmxAuth = ('admin', 'B4rc4123!')
  35. imgDir = 'maps/'
  36. markerDir = 'markers/'
  37. markerFiles = os.listdir(markerDir)
  38. markers = {}
  39. for filename in markerFiles:
  40. name = filename.split('.')[0]
  41. markers[name] = Image.open(markerDir + filename).convert("RGBA")
  42. markers[name].thumbnail((100, 100), Image.ANTIALIAS)
  43. # markerW, markerH = marker.size
  44. apiUri = {
  45. 'mac': '/api/location/v1/clients?macAddress=',
  46. 'ip': '/api/location/v2/clients?ipAddress=',
  47. 'map': '/api/config/v1/maps/imagesource/',
  48. 'ssid': '/api/location/v2/clients/?include=metadata&pageSize=1000&page=',
  49. 'count': '/api/location/v2/clients/count',
  50. 'floors': '/api/config/v1/maps',
  51. 'tag': '/api/location/v1/tags/'
  52. }
  53. def serve_pil_image(pil_img):
  54. img_io = BytesIO()
  55. pil_img.save(img_io, 'JPEG', quality=100)
  56. img_io.seek(0)
  57. return send_file(img_io, mimetype='image/jpeg')
  58. def getMap(imageName):
  59. imgFile = Path(imgDir + imageName)
  60. if not imgFile.is_file():
  61. respImg = requests.get(cmxBaseUrl + apiUri['map'] + imageName,
  62. auth=cmxAuth, verify=False, stream=True)
  63. if respImg.status_code == 200:
  64. with open(str(imgFile), 'wb') as f:
  65. for chunk in respImg:
  66. f.write(chunk)
  67. def getAllFloorMaps():
  68. response = requests.get(cmxBaseUrl + apiUri['floors'],
  69. auth=cmxAuth, verify=False)
  70. floorList = {}
  71. if response and response != '':
  72. try:
  73. dataDict = json.loads(response.text)
  74. if 'campuses' in dataDict:
  75. for campus in dataDict['campuses']:
  76. if'buildingList' in campus:
  77. for building in campus['buildingList']:
  78. for floor in building['floorList']:
  79. floorId = floor['aesUid']
  80. imageName = floor['image']['imageName']
  81. floorList[floorId] = imageName
  82. getMap(imageName)
  83. except:
  84. print ('Unexpected error' + str(sys.exc_info()[0]),
  85. file=sys.stderr)
  86. raise
  87. return floorList
  88. class CMX(Resource):
  89. def get(self):
  90. parser = reqparse.RequestParser()
  91. parser.add_argument('ip', help='IP address of the endpoint')
  92. parser.add_argument('ipv6', help='IPv6 address of the endpoint')
  93. parser.add_argument('mac', help='MAC address of the endpoint')
  94. parser.add_argument('marker', help='Marker used to display the location of the endpoint', default='marker')
  95. parser.add_argument('size', help='Size of the image returned')
  96. parser.add_argument('tag', help='Asset tag MAC address')
  97. args = parser.parse_args()
  98. response = ''
  99. if args.get('ip'):
  100. clientIp = args.get('ip')
  101. response = requests.get(cmxBaseUrl + apiUri['ip'] + clientIp,
  102. auth=cmxAuth, verify=False)
  103. elif args.get('tag'):
  104. clientMac = args.get('tag')
  105. response = requests.get(cmxBaseUrl + apiUri['tag'] + clientMac,
  106. auth=cmxAuth, verify=False)
  107. elif args.get('mac'):
  108. clientMac = args.get('mac')
  109. response = requests.get(cmxBaseUrl + apiUri['mac'] + clientMac,
  110. auth=cmxAuth, verify=False)
  111. elif args.get('ipv6'):
  112. clientIp = IPv6Address(args.get('ipv6')).exploded
  113. response = requests.get(cmxBaseUrl + apiUri['ip'] + clientIp,
  114. auth=cmxAuth, verify=False)
  115. markerName = args.get('marker')
  116. marker = markers['marker']
  117. if markerName in markers:
  118. marker = markers[markerName]
  119. markerW, markerH = marker.size
  120. if response and response != '':
  121. try:
  122. dataDict = json.loads(response.text)
  123. result = None
  124. if args.get('tag') and dataDict and 'mapInfo' in dataDict:
  125. result = dataDict
  126. elif len(dataDict) > 0 and 'mapInfo' in dataDict[0]:
  127. result = dataDict[0]
  128. if result is not None:
  129. imageName = result['mapInfo']['image']['imageName']
  130. mapLength = result['mapInfo']['floorDimension']['length']
  131. mapWidth = result['mapInfo']['floorDimension']['width']
  132. imageLength = result['mapInfo']['image']['height']
  133. imageWidth = result['mapInfo']['image']['width']
  134. coordX = result['mapCoordinate']['x']
  135. coordY = result['mapCoordinate']['y']
  136. positionX = (imageWidth / mapWidth)*coordX
  137. positionY = (imageLength / mapLength)*coordY
  138. getMap(imageName)
  139. im = Image.open(str(imgDir + imageName))
  140. positionX = positionX - markerW/2
  141. positionY = positionY - markerH
  142. offset = (int(positionX), int(positionY))
  143. im.paste(marker, offset, marker)
  144. if args.get('size'):
  145. # print('SIZE', file=sys.stderr)
  146. size = args.get('size')
  147. im.thumbnail((int(size), int(size)), Image.ANTIALIAS)
  148. return serve_pil_image(im)
  149. else:
  150. abort(404, 'Requested element not found')
  151. except Exception as inst:
  152. print ('Unexpected error with request= {} | error : {}'.format(response.text, inst), file=sys.stderr)
  153. return {'response': str(response.text), 'error': str(inst)}
  154. return abort(404, 'Missing parameter ip, ipv6 or mac. Other possible parameters are: marker (' + ', '.join(markers.keys()) + ')')
  155. class CMX_SSID(Resource):
  156. def get(self):
  157. parser = reqparse.RequestParser()
  158. parser.add_argument('ssid', help='SSID used by the clients')
  159. parser.add_argument('floor', help='Floor used by the clients')
  160. parser.add_argument('marker', help='Marker used to display the location of the endpoint', default='marker')
  161. args = parser.parse_args()
  162. response = ''
  163. if args.get('ssid'):
  164. ssid = args.get('ssid')
  165. countResp = requests.get(cmxBaseUrl + apiUri['count'],
  166. auth=cmxAuth, verify=False)
  167. try:
  168. dataDict = json.loads(countResp.text)
  169. if 'count' in dataDict:
  170. count = dataDict['count']
  171. maxPageId = (count // 1000) + 1
  172. print ('Count: {} MaxPage: {}'.format(count, maxPageId),
  173. file=sys.stderr)
  174. userList = {}
  175. floorList = getAllFloorMaps()
  176. for pageId in range(1, maxPageId):
  177. print ('Page: {} MaxPage: {}'.format(pageId, maxPageId),
  178. file=sys.stderr)
  179. response = requests.get(cmxBaseUrl + apiUri['ssid']
  180. + str(pageId), auth=cmxAuth,
  181. verify=False)
  182. if response and response.text != '':
  183. try:
  184. userDict = json.loads(response.text)
  185. for user in userDict:
  186. if user['ssId'] == ssid:
  187. floorName = user['mapInfo']['floorRefId']
  188. if floorName in userList:
  189. userList[floorName].append(user)
  190. else:
  191. userList[floorName] = [user]
  192. except:
  193. print ('Unexpected error with page request= '
  194. + response.text + ' | error : '
  195. + str(sys.exc_info()[0]),
  196. file=sys.stderr)
  197. return {'response': str(response.text), 'error': str(inst)}
  198. if args.get('floor'):
  199. floor = args.get('floor')
  200. if floor in userList:
  201. markerName = args.get('marker')
  202. marker = ''
  203. if markerName in markers:
  204. marker = markers[markerName]
  205. else:
  206. marker = markers['marker']
  207. markerW, markerH = marker.size
  208. imageName = floorList[floorName]
  209. getMap()
  210. im = Image.open(str(imgDir + imageName))
  211. for data in userList[floor]:
  212. mapInfo = data['mapInfo']
  213. mapLength = mapInfo['floorDimension']['length']
  214. mapWidth = mapInfo['floorDimension']['width']
  215. imageLength = mapInfo['image']['height']
  216. imageWidth = mapInfo['image']['width']
  217. coordX = data['mapCoordinate']['x']
  218. coordY = data['mapCoordinate']['y']
  219. positionX = (imageWidth / mapWidth)*coordX
  220. positionY = (imageLength / mapLength)*coordY
  221. positionX = positionX - markerW/2
  222. positionY = positionY - markerH
  223. offset = (int(positionX), int(positionY))
  224. im.paste(marker, offset, marker)
  225. return serve_pil_image(im)
  226. elif floor in floorList:
  227. imageName = floorList[floor]
  228. getMap(imageName)
  229. im = Image.open(str(imgDir + imageName))
  230. return serve_pil_image(im)
  231. else:
  232. abort(404)
  233. else:
  234. return list(floorList.keys())
  235. except Exception as inst:
  236. print ('Unexpected error with request= {} | error : {}'.format(countResp, inst), file=sys.stderr)
  237. return {'response': str(countResp), 'error': str(inst)}
  238. class sync(Resource):
  239. def get(self):
  240. return getAllFloorMaps()
  241. class home(Resource):
  242. def get(self):
  243. return {}
  244. api.add_resource(home, '/')
  245. api.add_resource(CMX, '/api/v0.1/cmx')
  246. api.add_resource(CMX_SSID, '/api/v0.1/ssid')
  247. api.add_resource(sync, '/api/v0.1/sync')
  248. if __name__ == '__main__':
  249. app.run(host='10.100.253.13', debug=True, port=8002, threaded=True)