PageRenderTime 72ms CodeModel.GetById 36ms RepoModel.GetById 0ms app.codeStats 0ms

/lab-3tier-lb/httpclients.py

https://github.com/vincentbernat/network-lab
Python | 101 lines | 79 code | 6 blank | 16 comment | 18 complexity | 29ec93944fcf1dd5eb83ddd934263e7e MD5 | raw file
  1. #!/usr/bin/env python3
  2. """Simulate many clients toward a given set of IP services. Provide a
  3. small graph on screen to show when errors occurs. IPs of the clients
  4. should be on the loopback interface. The service IPs are provided on
  5. the command-line.
  6. For example::
  7. ./httpclients.py \
  8. 198.51.100.1:80 \
  9. 198.51.100.2:80 \
  10. [2001:db8::198.51.100.1]:80 \
  11. [2001:db8::198.51.100.2]:80
  12. """
  13. import aiohttp
  14. import asyncio
  15. import json
  16. import random
  17. import subprocess
  18. import sys
  19. async def fetch(source, destination):
  20. """HTTP request to the provided destination (IP, port) using the
  21. provided source (IP, port)."""
  22. host, port = destination
  23. url = f'http://[{host}]:{port}/10M'
  24. conn = aiohttp.TCPConnector(local_addr=(source, 0))
  25. async with aiohttp.ClientSession(connector=conn) as client:
  26. async with client.get(url) as resp:
  27. while True:
  28. await asyncio.sleep(random.random()*5)
  29. got = await asyncio.wait_for(resp.content.readany(), timeout=2)
  30. if not got or random.random() > 0.95:
  31. break
  32. del got
  33. async def main(sources, destinations):
  34. parallel = 50
  35. pending = set()
  36. failures = 0
  37. successes = 0
  38. while True:
  39. source = random.choice(sources)
  40. destination = random.choice(destinations)
  41. if ':' in source and ':' not in destination[0]:
  42. continue
  43. if ':' in destination[0] and ':' not in source:
  44. continue
  45. pending.add(fetch(source, destination))
  46. if len(pending) >= parallel:
  47. done, pending = await asyncio.wait(
  48. pending,
  49. return_when=asyncio.FIRST_COMPLETED)
  50. for d in done:
  51. try:
  52. await d
  53. except:
  54. failures += 1
  55. else:
  56. successes += 1
  57. if failures + successes == 8:
  58. if successes == 0:
  59. c = "\033[1;31m_\033[0;0m"
  60. elif successes == 8:
  61. c = "\033[1;32m⣿\033[0;0m"
  62. else:
  63. c = "\033[1;31m{}\033[0;0m".format(
  64. chr(0x2800 + (1 << 8) - (1 << failures)))
  65. print(c, end='', flush=True)
  66. failures = 0
  67. successes = 0
  68. def parse_destination(destination):
  69. ip, port = destination.rsplit(":", 1)
  70. if ip[0] == '[' and ip[-1] == ']':
  71. ip = ip[1:-1]
  72. return (ip, int(port))
  73. def get_sources():
  74. s = subprocess.run(["ip", "-json", "addr", "list", "dev", "lo"],
  75. stdout=subprocess.PIPE,
  76. check=True)
  77. info = json.loads(s.stdout.decode('utf-8'))
  78. return [addr['local']
  79. for addr in info[0]['addr_info']
  80. if addr['scope'] == 'global']
  81. destinations = [parse_destination(arg) for arg in sys.argv[1:]]
  82. sources = get_sources()
  83. loop = asyncio.get_event_loop()
  84. try:
  85. loop.run_until_complete(main(sources, destinations))
  86. except KeyboardInterrupt:
  87. pass