Advertisement
Guest User

Untitled

a guest
May 6th, 2016
53
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.68 KB | None | 0 0
  1. #!/usr/bin/python
  2. """Get the set of instance id's (exclude spot instances)!"""
  3. import boto3
  4. import datetime
  5. import json
  6. import redis
  7. import sys
  8. import base64
  9. VERSION = 0.2
  10.  
  11. import base64
  12. import struct
  13.  
  14. def encode(n):
  15. n = int(n[2:], 16)
  16. data = struct.pack('<Q', n).rstrip('\x00')
  17. if len(data) == 0:
  18. data = '\x00'
  19. s = base64.urlsafe_b64encode(data).rstrip('=')
  20. return "{:>6}".format(s) # right just with spaces?
  21.  
  22. def decode(s):
  23. data = base64.urlsafe_b64decode(s + '==')
  24. n = struct.unpack('<Q', data + '\x00'* (8-len(data)) )
  25. return "i-{:>08}".format(hex(n[0])[2:])
  26.  
  27.  
  28. def cache(innerfunc):
  29. redis_conn = redis.StrictRedis()
  30. key = (innerfunc.__name__, VERSION)
  31. def wrapped():
  32. cached_val = redis_conn.get(key)
  33. if cached_val:
  34. val = json.loads(cached_val)
  35. else:
  36. val = innerfunc()
  37. redis_conn.setex(key, 60*30, json.dumps(val))
  38. return val
  39. return wrapped
  40.  
  41.  
  42. def datetime_to_timestamp(idatetime):
  43. if isinstance(idatetime, datetime.datetime):
  44. epoch = datetime.datetime.utcfromtimestamp(0)
  45. return (idatetime.replace(tzinfo=None) - epoch).total_seconds()
  46. else:
  47. return idatetime
  48.  
  49. def recursive_date_to_epoch(obj):
  50. """find any date time objects and transform them into time since epoch"""
  51. if isinstance(obj, dict):
  52. for key, val in obj.iteritems():
  53. obj[key] = datetime_to_timestamp(val)
  54. recursive_date_to_epoch(val)
  55. elif isinstance(obj, list):
  56. for i, j in enumerate(obj):
  57. obj[i] = datetime_to_timestamp(j)
  58. recursive_date_to_epoch(j)
  59.  
  60. @cache
  61. def get_running_instances():
  62. conn = boto3.client('ec2', 'us-east-1')
  63. response = conn.describe_instances(
  64. Filters=[
  65. {
  66. 'Name': 'instance-state-name',
  67. 'Values': [
  68. 'running',
  69. ]
  70. },
  71. ],
  72. )['Reservations']
  73. recursive_date_to_epoch(response)
  74. all_instances = list()
  75. for i in response:
  76. all_instances.extend(i['Instances'])
  77. non_spot_instances = [x for x in all_instances if x.get("InstanceLifecycle") != "spot"]
  78. return non_spot_instances
  79.  
  80. def tag_reservation(obj_id, tag_key, tag_val):
  81. conn = boto3.client('ec2', 'us-east-1')
  82. response = conn.create_tags(
  83. Resources=[
  84. obj_id,
  85. ],
  86. Tags=[
  87. {
  88. 'Key': tag_key,
  89. 'Value': tag_val
  90. },
  91. ]
  92. )
  93.  
  94. #@cache
  95. def get_reservations():
  96. conn = boto3.client('ec2', 'us-east-1')
  97. response = conn.describe_reserved_instances(
  98. Filters=[
  99. {
  100. 'Name': 'state',
  101. 'Values': [
  102. 'active'
  103. ]
  104. }
  105. ]
  106. )['ReservedInstances']
  107. recursive_date_to_epoch(response)
  108. return response
  109.  
  110. def partition(iterable, func):
  111. ans = dict()
  112. for i in iterable:
  113. key = func(i)
  114. ans.setdefault(key, list())
  115. ans[key].append(i)
  116. return ans
  117.  
  118. def unpack(packed_string):
  119. ans = list()
  120. try:
  121. for startpos in xrange(0, len(packed_string), 6):
  122. packed_id = packed_string[startpos: startpos+6]
  123. ans.append(decode(packed_id))
  124. return sorted(ans)
  125. except Exception as oops:
  126. return list()
  127.  
  128. class ReservationManager(object):
  129. instance_group_function = lambda x: (x['InstanceType'], x['Placement']['AvailabilityZone'])
  130. res_group_function = lambda x: (x['InstanceType'], x['AvailabilityZone'])
  131. def __init__(self):
  132. self.running_instances = get_running_instances()
  133. self.partitioned_instances = partition(self.running_instances, self.instance_group_function)
  134. self.clean_instances()
  135. self.reservations = get_reservations()
  136. self.partitioned_reservations = partition(self.reservations, self.res_group_function)
  137. self.clean_reservations()
  138. self.instance_to_reservation = dict()
  139. # filter out reserved instances? and remove used reservations
  140. self.reservation_to_instances = dict()
  141. for res in self.reservations:
  142. res_id = res['ReservedInstancesId']
  143. self.reservation_to_instances[res_id] = set()
  144. self.iid_to_instance = {instance['InstanceId']: instance for instance in self.running_instances}
  145. self.sync_with_saved()
  146.  
  147. def sync_with_saved(self):
  148. self.clean_instances()
  149. for instance in self.running_instances:
  150. res_id = instance.get('Tags', {}).get('reservation_id')
  151. instance_id = instance['InstanceId']
  152. try:
  153. self.reservation_to_instances[res_id].add(instance_id)
  154. except:
  155. pass # you land here when the reservation has expired
  156. for res in self.reservations:
  157. res_id = res['ReservedInstancesId']
  158. res.setdefault('Tags', dict())
  159. tags = res.get('Tags', {})
  160. current_instances = tags.get('instances', '')
  161. unpacked = unpack(current_instances)
  162. res['Tags']['instances'] = set(unpacked)
  163. for iid in unpacked:
  164. if iid in self.iid_to_instance: # i.e. it's a running instance
  165. self.instance_to_reservation[iid] = res_id
  166.  
  167. def clean_instances(self):
  168. for instance in self.running_instances:
  169. for key in ["NetworkInterfaces", "BlockDeviceMappings", "SecurityGroups", "IamInstanceProfile", "Monitoring", "State"]:
  170. instance.pop(key, None)
  171. tag_list = instance.get('Tags')
  172. if type(tag_list) == type(list()):
  173. tag_dict = dict([(x['Key'], x['Value']) for x in tag_list])
  174. instance['Tags'] = tag_dict
  175.  
  176. def clean_reservations(self):
  177. for res in self.reservations:
  178. tag_list = res.get('Tags')
  179. if type(tag_list) == type(list()):
  180. tag_dict = dict([(x['Key'], x['Value']) for x in tag_list])
  181. res['Tags'] = tag_dict
  182.  
  183. def update_for_partition(self, partition):
  184. (instance_type, az) = partition
  185. instances = self.partitioned_instances[partition]
  186. instances.sort(key=lambda x: x['LaunchTime']) # reserve oldest instance first
  187. reservations = self.partitioned_reservations[partition]
  188. reservations.sort(key=lambda x: x['End'], reverse=True) # sort from ending furthest in the future to soonest
  189. first_unreserved_index = 0
  190. total_slots = sum([x['InstanceCount'] for x in reservations])
  191. for reservation in reservations:
  192. res_id = reservation['ReservedInstancesId']
  193. for slot in xrange(reservation['InstanceCount']):
  194. try:
  195. instance = instances[first_unreserved_index]
  196. except IndexError:
  197. return
  198. instance_id = instance['InstanceId']
  199. first_unreserved_index += 1
  200. self.reservation_to_instances[res_id].add(instance_id)
  201.  
  202. def tag_reservations(self):
  203. for res in self.reservations:
  204. res_id = res['ReservedInstancesId']
  205. old_instances = sorted(res['Tags']['instances'])
  206. new_instances = sorted(self.reservation_to_instances[res_id])
  207. if old_instances != new_instances:
  208. new_tag = "".join([encode(instance) for instance in new_instances])
  209. tag_reservation(res_id, 'instances', new_tag)
  210. else:
  211. pass
  212.  
  213. def adjust_single_reservation(self, res_id):
  214. print >> sys.stderr, "Would update {}".format(res_id)
  215. pass
  216.  
  217. def adjust_reservations(self):
  218. for res in self.reservations:
  219. res_id = res['ReservedInstancesId']
  220. slots = res['InstanceCount']
  221. instance_type = res['InstanceType']
  222. used = len(self.reservation_to_instances[res_id])
  223. if slots != used:
  224. print "{} of {} {} reserved for {}".format(used, instance_type, slots, res_id)
  225. self.adjust_single_reservation(res_id)
  226. # figure out how to use the excess here...
  227.  
  228. def update_reserved_instances(self):
  229. for partition in sorted(set(self.partitioned_instances) & set(self.partitioned_reservations)):
  230. self.update_for_partition(partition)
  231. for key, val in self.reservation_to_instances.iteritems():
  232. self.reservation_to_instances[key] = sorted(val)
  233.  
  234. # see if there are reservations which are not used up...
  235. self.adjust_reservations()
  236.  
  237.  
  238. # update reservation configurations... (ONLY FOR THOSE WHICH CHANGED)
  239.  
  240.  
  241. # tag both ways...
  242. self.tag_reservations()
  243.  
  244.  
  245.  
  246. def main():
  247. reservation_manager = ReservationManager()
  248. reservation_manager.update_reserved_instances()
  249.  
  250. if "__main__" == __name__:
  251. main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement