/simulator/topology.py

https://bitbucket.org/hilfialkaff/tomography · Python · 405 lines · 281 code · 82 blank · 42 comment · 67 complexity · 73c2c8e2d454ccb2240a0a54e5f359ee MD5 · raw file

  1. from random import randrange, choice, seed
  2. from graph import Graph
  3. from node import Node
  4. class Topology(object):
  5. def __init__(self):
  6. self.bandwidth = 0
  7. def generate_graph(self):
  8. raise NotImplementedError('Method is unimplemented in abstract class!')
  9. def k_path_validity(self, path):
  10. return True
  11. def k_path_heuristic(self, path):
  12. return 0
  13. def get_bandwidth(self):
  14. return self.bandwidth
  15. @staticmethod
  16. def get_name():
  17. raise NotImplementedError("Method unimplemented in abstract class...")
  18. class JellyfishTopology(Topology):
  19. def __init__(self, bandwidth, num_hosts, num_switches, num_ports):
  20. super(JellyfishTopology, self).__init__()
  21. self.bandwidth = bandwidth
  22. self.num_hosts = num_hosts
  23. self.num_switches = num_switches
  24. self.num_ports = num_ports
  25. @staticmethod
  26. def get_name():
  27. return "JF"
  28. def generate_graph(self):
  29. ''' Generate a Jellyfish topology-like graph
  30. @param num_hosts number of hosts
  31. @param num_switches number of switches
  32. @param num_ports number of ports in each switch
  33. @param s seed to be used for RNG
  34. @return dictionary of nodes and edges representing the topology
  35. '''
  36. seed(1) # for Random Number Generation
  37. hosts = []
  38. switches = []
  39. open_ports = []
  40. bandwidth = self.bandwidth
  41. num_hosts = self.num_hosts
  42. num_switches = self.num_switches
  43. num_ports = self.num_ports
  44. assert(num_switches >= num_hosts)
  45. assert(num_ports > 1)
  46. graph = Graph(bandwidth)
  47. # add hosts
  48. for i in range(1, num_hosts + 1):
  49. node_id = "h%s" % i
  50. new_host = Node(node_id)
  51. hosts.append(new_host)
  52. graph.add_node(new_host)
  53. # add switches
  54. for i in range(1, num_switches + 1):
  55. node_id = "s%s" % i
  56. new_switch = Node(node_id)
  57. open_ports.append(num_ports)
  58. new_switch = Node(node_id)
  59. switches.append(new_switch)
  60. graph.add_node(new_switch)
  61. # connect each server with a switch
  62. for i in range(num_hosts):
  63. graph.add_link(hosts[i], switches[i], bandwidth)
  64. open_ports[i] -= 1
  65. links = set()
  66. switches_left = num_switches
  67. consec_fails = 0
  68. while switches_left > 1 and consec_fails < 10:
  69. s1 = randrange(num_switches)
  70. while open_ports[s1] == 0:
  71. s1 = randrange(num_switches)
  72. s2 = randrange(num_switches)
  73. while open_ports[s2] == 0 or s1 == s2:
  74. s2 = randrange(num_switches)
  75. if (s1, s2) in links:
  76. consec_fails += 1
  77. else:
  78. consec_fails = 0
  79. links.add((s1, s2))
  80. links.add((s2, s1))
  81. open_ports[s1] -= 1
  82. open_ports[s2] -= 1
  83. if open_ports[s1] == 0:
  84. switches_left -= 1
  85. if open_ports[s2] == 0:
  86. switches_left -= 1
  87. if switches_left > 0:
  88. for i in range(num_switches):
  89. while open_ports[i] > 1:
  90. while True:
  91. rLink = choice(list(links))
  92. if (i, rLink[0]) in links:
  93. continue
  94. if (i, rLink[1]) in links:
  95. continue
  96. #remove links
  97. links.remove(rLink)
  98. links.remove(rLink[::-1])
  99. # add new links
  100. links.add((i, rLink[0]))
  101. links.add((rLink[0], i))
  102. links.add((i, rLink[1]))
  103. links.add((rLink[1], i))
  104. open_ports[i] -= 2
  105. break
  106. for link in links:
  107. # prevent double counting
  108. if link[0] < link[1]:
  109. graph.add_link(switches[link[0]], switches[link[1]], bandwidth)
  110. graph.set_k_path_heuristic(self.k_path_heuristic)
  111. graph.set_k_path_validity(self.k_path_validity)
  112. return graph
  113. class Jellyfish2Topology(Topology):
  114. def __init__(self, bandwidth, num_hosts, num_switches, num_ports):
  115. super(Jellyfish2Topology, self).__init__()
  116. self.bandwidth = bandwidth
  117. self.num_hosts = num_hosts
  118. self.num_switches = num_switches
  119. self.num_ports = num_ports
  120. @staticmethod
  121. def get_name():
  122. return "JF2"
  123. def generate_graph(self):
  124. ''' Generate a Jellyfish topology-like graph
  125. @param num_hosts number of hosts
  126. @param num_switches number of switches
  127. @param num_ports number of ports in each switch
  128. @param s seed to be used for RNG
  129. @return dictionary of nodes and edges representing the topology
  130. '''
  131. seed(1) # for Random Number Generation
  132. hosts = []
  133. switches = []
  134. open_ports = []
  135. bandwidth = self.bandwidth
  136. num_hosts = self.num_hosts
  137. num_switches = self.num_switches
  138. num_ports = self.num_ports
  139. assert(num_switches >= num_hosts)
  140. assert(num_ports > 1)
  141. graph = Graph(bandwidth)
  142. # add hosts
  143. for i in range(1, num_hosts + 1):
  144. node_id = "h%s" % i
  145. new_host = Node(node_id)
  146. hosts.append(new_host)
  147. graph.add_node(new_host)
  148. # add switches
  149. for i in range(1, num_switches + 1):
  150. node_id = "s%s" % i
  151. new_switch = Node(node_id)
  152. open_ports.append(num_ports)
  153. new_switch = Node(node_id)
  154. switches.append(new_switch)
  155. graph.add_node(new_switch)
  156. # connect each server with a switch
  157. for i in range(num_hosts):
  158. graph.add_link(hosts[i], switches[i], bandwidth)
  159. open_ports[i] -= 1
  160. # Modified jellyfish: add "supernodes"
  161. for i in range(num_hosts):
  162. add_switch = 1
  163. added_switch = 0
  164. # TODO: Probability-based switch additions
  165. # if random() > 0.75:
  166. # add_switch = 0
  167. # elif random() > 0.50:
  168. # add_switch = 1
  169. # elif random() > 0.25:
  170. # add_switch = 2
  171. # else:
  172. # add_switch = 3
  173. while added_switch != add_switch:
  174. switch_id = randrange(num_switches)
  175. if graph.get_link(hosts[i], switches[switch_id]):
  176. continue
  177. graph.add_link(hosts[i], switches[switch_id], bandwidth)
  178. open_ports[switch_id] -= 1
  179. added_switch += 1
  180. links = set()
  181. switches_left = num_switches
  182. consec_fails = 0
  183. while switches_left > 1 and consec_fails < 10:
  184. s1 = randrange(num_switches)
  185. while open_ports[s1] == 0:
  186. s1 = randrange(num_switches)
  187. s2 = randrange(num_switches)
  188. while open_ports[s2] == 0 or s1 == s2:
  189. s2 = randrange(num_switches)
  190. if (s1, s2) in links:
  191. consec_fails += 1
  192. else:
  193. consec_fails = 0
  194. links.add((s1, s2))
  195. links.add((s2, s1))
  196. open_ports[s1] -= 1
  197. open_ports[s2] -= 1
  198. if open_ports[s1] == 0:
  199. switches_left -= 1
  200. if open_ports[s2] == 0:
  201. switches_left -= 1
  202. if switches_left > 0:
  203. for i in range(num_switches):
  204. while open_ports[i] > 1:
  205. while True:
  206. rLink = choice(list(links))
  207. if (i, rLink[0]) in links:
  208. continue
  209. if (i, rLink[1]) in links:
  210. continue
  211. #remove links
  212. links.remove(rLink)
  213. links.remove(rLink[::-1])
  214. # add new links
  215. links.add((i, rLink[0]))
  216. links.add((rLink[0], i))
  217. links.add((i, rLink[1]))
  218. links.add((rLink[1], i))
  219. open_ports[i] -= 2
  220. break
  221. for link in links:
  222. # prevent double counting
  223. if link[0] < link[1]:
  224. graph.add_link(switches[link[0]], switches[link[1]], bandwidth)
  225. graph.set_k_path_heuristic(self.k_path_heuristic)
  226. graph.set_k_path_validity(self.k_path_validity)
  227. return graph
  228. class FatTreeTopology(Topology):
  229. def __init__(self, bandwidth, num_ports):
  230. super(FatTreeTopology, self).__init__()
  231. self.bandwidth = bandwidth
  232. self.num_ports = num_ports
  233. @staticmethod
  234. def get_name():
  235. return "FT"
  236. def k_path_validity(self, path):
  237. se_count = 0
  238. sa_count = 0
  239. sc_count = 0
  240. h_count = 0
  241. ret = True
  242. # print "path: ", path
  243. for p in path:
  244. if 'se' in p:
  245. se_count += 1
  246. if 'sa' in p:
  247. sa_count += 1
  248. if 'sc' in p:
  249. sc_count += 1
  250. if 'h' in p:
  251. h_count += 1
  252. if h_count > 2:
  253. ret = False
  254. break
  255. if sc_count == 0 and (sa_count > 1 or se_count > 1):
  256. ret = False
  257. break
  258. # print "count: ", se_count, sa_count, sc_count, h_count
  259. return ret
  260. def k_path_heuristic(self, path):
  261. return 0
  262. se_count = 0
  263. sa_count = 0
  264. heuristic = 0
  265. for p in path:
  266. if 'se' in p:
  267. se_count += 1
  268. if 'sa' in p:
  269. sa_count += 1
  270. if 'se' > 2:
  271. heuristic += se_count - 2
  272. if 'sa' > 2:
  273. heuristic += sa_count - 2
  274. return heuristic
  275. def generate_graph(self):
  276. ''' Generate a Fat Tree topology-like graph
  277. @param num_ports number of ports in each switch
  278. @return dictionary of nodes and edges representing the topology
  279. '''
  280. hosts = []
  281. bandwidth = self.bandwidth
  282. num_ports = self.num_ports
  283. pods = range(0, num_ports)
  284. core_sws = range(1, num_ports/2 + 1)
  285. agg_sws = range(num_ports/2, num_ports)
  286. edge_sws = range(0, num_ports/2)
  287. hosts = range(2, num_ports/2 + 2)
  288. graph = Graph(bandwidth)
  289. for p in pods:
  290. for e in edge_sws:
  291. edge_id = "se_%i_%i_%i" % (p, e, 1)
  292. edge_switch = graph.get_node(edge_id)
  293. if not edge_switch:
  294. edge_switch = Node(edge_id)
  295. graph.add_node(edge_switch)
  296. for h in hosts:
  297. host_id = "h_%i_%i_%i" % (p, e, h)
  298. host = graph.get_node(host_id)
  299. if not host:
  300. host = Node(host_id)
  301. graph.add_node(host)
  302. graph.add_link(edge_switch, host, bandwidth)
  303. for a in agg_sws:
  304. agg_id = "sa_%i_%i_%i" %(p, a, 1)
  305. agg_switch = graph.get_node(agg_id)
  306. if not agg_switch:
  307. agg_switch = Node(agg_id)
  308. graph.add_node(agg_switch)
  309. graph.add_link(agg_switch, edge_switch, bandwidth)
  310. for a in agg_sws:
  311. agg_id = "sa_%i_%i_%i" %(p, a, 1)
  312. agg_switch = graph.get_node(agg_id)
  313. c_index = a - num_ports / 2 + 1
  314. for c in core_sws:
  315. core_id = "sc_%i_%i_%i" % (num_ports, c_index, c)
  316. core_switch = graph.get_node(core_id)
  317. if not core_switch:
  318. core_switch = Node(core_id)
  319. graph.add_node(core_switch)
  320. graph.add_link(core_switch, agg_switch, bandwidth)
  321. graph.set_k_path_heuristic(self.k_path_heuristic)
  322. graph.set_k_path_validity(self.k_path_validity)
  323. return graph