update_dhcp.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. #!/usr/bin/env python3
  2. #
  3. # Copyright (c) 2017-2019 Joe Clarke <jclarke@cisco.com>
  4. # All rights reserved.
  5. #
  6. # Redistribution and use in source and binary forms, with or without
  7. # modification, are permitted provided that the following conditions
  8. # are met:
  9. # 1. Redistributions of source code must retain the above copyright
  10. # notice, this list of conditions and the following disclaimer.
  11. # 2. Redistributions in binary form must reproduce the above copyright
  12. # notice, this list of conditions and the following disclaimer in the
  13. # documentation and/or other materials provided with the distribution.
  14. #
  15. # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  16. # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  18. # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  19. # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20. # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  21. # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  22. # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  23. # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  24. # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  25. # SUCH DAMAGE.
  26. from builtins import str
  27. from builtins import range
  28. import json
  29. import requests
  30. from requests.packages.urllib3.exceptions import InsecureRequestWarning
  31. requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
  32. import sys
  33. import re
  34. from netaddr import IPAddress
  35. import CLEUCreds
  36. from cleu.config import Config as C
  37. IDF_CNT = 99
  38. ADDITIONAL_IDFS = (252, 253, 254)
  39. FIRST_IP = 31
  40. LAST_IP = 253
  41. IDF_OVERRIDES = {
  42. 252: {"first_ip": 160, "last_ip": "250"},
  43. 253: {"first_ip": 160, "last_ip": "250"},
  44. 254: {"first_ip": 160, "last_ip": "250"},
  45. }
  46. SCOPE_BASE = C.DHCP_BASE + "Scope"
  47. DHCP_TEMPLATE = {"optionList": {"OptionItem": []}}
  48. HEADERS = {"authorization": CLEUCreds.JCLARKE_BASIC, "accept": "application/json", "content-type": "application/json"}
  49. def mtoc(mask):
  50. return IPAddress(mask).netmask_bits()
  51. if __name__ == "__main__":
  52. if len(sys.argv) != 2:
  53. sys.stderr.write("usage: {} INPUT_FILE\n".format(sys.argv[0]))
  54. sys.exit(1)
  55. contents = None
  56. try:
  57. fd = open(sys.argv[1], "r")
  58. contents = fd.read()
  59. fd.close()
  60. except Exception as e:
  61. sys.stderr.write("Failed to open {}: {}\n".format(sys.argv[1], str(e)))
  62. sys.exit(1)
  63. for row in contents.split("\n"):
  64. row = row.strip()
  65. if re.search(r"^#", row):
  66. continue
  67. if row == "":
  68. continue
  69. [vlan, mask, name, policy] = row.split(",")
  70. if vlan == "" or mask == "" or name == "" or policy == "":
  71. sys.stderr.write("Skipping malformed row '{}'\n".format(row))
  72. continue
  73. start = 1
  74. cnt = IDF_CNT
  75. idf_set = ()
  76. if mask == "255.255.0.0":
  77. start = 0
  78. cnt = 0
  79. for i in range(start, cnt + 1):
  80. idf_set += (i,)
  81. if mask != "255.255.0.0":
  82. idf_set += ADDITIONAL_IDFS
  83. if mask.startswith("10."):
  84. octets = mask.split(".")
  85. idf_set = (octets[2],)
  86. mask = "255.255.255.0"
  87. for i in idf_set:
  88. prefix = "IDF-{}".format(str(i).zfill(3))
  89. if i == 0:
  90. prefix = "CORE"
  91. scope = ("{}-{}".format(prefix, name)).upper()
  92. ip = "10.{}.{}.0".format(vlan, i)
  93. octets = ["10", vlan, str(i), "0"]
  94. roctets = list(octets)
  95. roctets[3] = "254"
  96. url = "{}/{}".format(SCOPE_BASE, scope)
  97. response = requests.request("GET", url, headers=HEADERS, verify=False)
  98. if response.status_code != 404:
  99. sys.stderr.write("Scope {} already exists: {}\n".format(scope, response.status_code))
  100. continue
  101. template = {"optionList": {"OptionItem": []}}
  102. if mask == "255.255.0.0":
  103. roctets[2] = "255"
  104. template["optionList"]["OptionItem"].append({"number": "3", "value": ".".join(roctets)})
  105. first_ip = FIRST_IP
  106. last_ip = LAST_IP
  107. if i in IDF_OVERRIDES:
  108. first_ip = IDF_OVERRIDES[i]["first_ip"]
  109. last_ip = IDF_OVERRIDES[i]["last_ip"]
  110. sipa = list(octets)
  111. sipa[3] = str(first_ip)
  112. eipa = list(octets)
  113. eipa[3] = str(last_ip)
  114. if mask == "255.255.0.0":
  115. eipa[2] = "255"
  116. sip = ".".join(sipa)
  117. eip = ".".join(eipa)
  118. rlist = {"RangeItem": [{"end": eip, "start": sip}]}
  119. cidr = mtoc(mask)
  120. payload = {
  121. "embeddedPolicy": template,
  122. "name": scope,
  123. "policy": policy,
  124. "rangeList": rlist,
  125. "subnet": "{}/{}".format(ip, cidr),
  126. "tenantId": "0",
  127. "vpnId": "0",
  128. }
  129. try:
  130. response = requests.request("PUT", url, data=json.dumps(payload), headers=HEADERS, verify=False)
  131. response.raise_for_status()
  132. except Exception as e:
  133. sys.stderr.write(
  134. "Error adding scope {} ({}/{}) with range sip:{} eip:{}: {} ({})\n".format(
  135. scope, ip, cidr, sip, eip, response.text, str(e)
  136. )
  137. )
  138. sys.stderr.write("Request: {}\n".format(json.dumps(payload, indent=4)))
  139. continue