123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169 |
- #!/usr/bin/env python3
- #
- # Copyright (c) 2017-2019 Joe Clarke <jclarke@cisco.com>
- # All rights reserved.
- #
- # Redistribution and use in source and binary forms, with or without
- # modification, are permitted provided that the following conditions
- # are met:
- # 1. Redistributions of source code must retain the above copyright
- # notice, this list of conditions and the following disclaimer.
- # 2. Redistributions in binary form must reproduce the above copyright
- # notice, this list of conditions and the following disclaimer in the
- # documentation and/or other materials provided with the distribution.
- #
- # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- # SUCH DAMAGE.
- from builtins import str
- from builtins import range
- import json
- import requests
- from requests.packages.urllib3.exceptions import InsecureRequestWarning
- requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
- import sys
- import re
- from netaddr import IPAddress
- import CLEUCreds
- from cleu.config import Config as C
- IDF_CNT = 99
- ADDITIONAL_IDFS = (252, 253, 254)
- FIRST_IP = 31
- LAST_IP = 253
- IDF_OVERRIDES = {
- 252: {"first_ip": 160, "last_ip": "250"},
- 253: {"first_ip": 160, "last_ip": "250"},
- 254: {"first_ip": 160, "last_ip": "250"},
- }
- SCOPE_BASE = C.DHCP_BASE + "Scope"
- DHCP_TEMPLATE = {"optionList": {"OptionItem": []}}
- HEADERS = {"authorization": CLEUCreds.JCLARKE_BASIC, "accept": "application/json", "content-type": "application/json"}
- def mtoc(mask):
- return IPAddress(mask).netmask_bits()
- if __name__ == "__main__":
- if len(sys.argv) != 2:
- sys.stderr.write("usage: {} INPUT_FILE\n".format(sys.argv[0]))
- sys.exit(1)
- contents = None
- try:
- fd = open(sys.argv[1], "r")
- contents = fd.read()
- fd.close()
- except Exception as e:
- sys.stderr.write("Failed to open {}: {}\n".format(sys.argv[1], str(e)))
- sys.exit(1)
- for row in contents.split("\n"):
- row = row.strip()
- if re.search(r"^#", row):
- continue
- if row == "":
- continue
- [vlan, mask, name, policy] = row.split(",")
- if vlan == "" or mask == "" or name == "" or policy == "":
- sys.stderr.write("Skipping malformed row '{}'\n".format(row))
- continue
- start = 1
- cnt = IDF_CNT
- idf_set = ()
- if mask == "255.255.0.0":
- start = 0
- cnt = 0
- for i in range(start, cnt + 1):
- idf_set += (i,)
- if mask != "255.255.0.0":
- idf_set += ADDITIONAL_IDFS
- if mask.startswith("10."):
- octets = mask.split(".")
- idf_set = (octets[2],)
- mask = "255.255.255.0"
- for i in idf_set:
- prefix = "IDF-{}".format(str(i).zfill(3))
- if i == 0:
- prefix = "CORE"
- scope = ("{}-{}".format(prefix, name)).upper()
- ip = "10.{}.{}.0".format(vlan, i)
- octets = ["10", vlan, str(i), "0"]
- roctets = list(octets)
- roctets[3] = "254"
- url = "{}/{}".format(SCOPE_BASE, scope)
- response = requests.request("GET", url, headers=HEADERS, verify=False)
- if response.status_code != 404:
- sys.stderr.write("Scope {} already exists: {}\n".format(scope, response.status_code))
- continue
- template = {"optionList": {"OptionItem": []}}
- if mask == "255.255.0.0":
- roctets[2] = "255"
- template["optionList"]["OptionItem"].append({"number": "3", "value": ".".join(roctets)})
- first_ip = FIRST_IP
- last_ip = LAST_IP
- if i in IDF_OVERRIDES:
- first_ip = IDF_OVERRIDES[i]["first_ip"]
- last_ip = IDF_OVERRIDES[i]["last_ip"]
- sipa = list(octets)
- sipa[3] = str(first_ip)
- eipa = list(octets)
- eipa[3] = str(last_ip)
- if mask == "255.255.0.0":
- eipa[2] = "255"
- sip = ".".join(sipa)
- eip = ".".join(eipa)
- rlist = {"RangeItem": [{"end": eip, "start": sip}]}
- cidr = mtoc(mask)
- payload = {
- "embeddedPolicy": template,
- "name": scope,
- "policy": policy,
- "rangeList": rlist,
- "subnet": "{}/{}".format(ip, cidr),
- "tenantId": "0",
- "vpnId": "0",
- }
- try:
- response = requests.request("PUT", url, data=json.dumps(payload), headers=HEADERS, verify=False)
- response.raise_for_status()
- except Exception as e:
- sys.stderr.write(
- "Error adding scope {} ({}/{}) with range sip:{} eip:{}: {} ({})\n".format(
- scope, ip, cidr, sip, eip, response.text, str(e)
- )
- )
- sys.stderr.write("Request: {}\n".format(json.dumps(payload, indent=4)))
- continue
|