diff options
-rw-r--r-- | junos-bootstrap/README.md | 8 | ||||
-rw-r--r-- | junos-bootstrap/httpd/ex2200.template | 73 | ||||
-rw-r--r-- | junos-bootstrap/httpd/postgres_queries | 20 | ||||
-rw-r--r-- | junos-bootstrap/httpd/server_http.py | 110 | ||||
-rw-r--r-- | junos-bootstrap/httpd/terminal.log | 34 | ||||
-rw-r--r-- | junos-bootstrap/proof_of_concepts/tg15-tech82-poc1.tar.gz | bin | 0 -> 4691 bytes |
6 files changed, 245 insertions, 0 deletions
diff --git a/junos-bootstrap/README.md b/junos-bootstrap/README.md new file mode 100644 index 0000000..77d125a --- /dev/null +++ b/junos-bootstrap/README.md @@ -0,0 +1,8 @@ +junos-bootstrap +=============== + +(a.k.a. juniper-bootstrap, JunOSBootStrapper) + +Tools (DHCPDd + HTTPD)for managing a large number of factory default Juniper switches (EX2200) using ZTP (Zero Touch Protocol). + +The project is built with Python and PostgreSQL. diff --git a/junos-bootstrap/httpd/ex2200.template b/junos-bootstrap/httpd/ex2200.template new file mode 100644 index 0000000..b786f64 --- /dev/null +++ b/junos-bootstrap/httpd/ex2200.template @@ -0,0 +1,73 @@ +system { + host-name $hostname; + root-authentication { + encrypted-password "$1$oQTnGCDI$UZpSpT5z7uHhFvniCzY5w/"; ## SECRET-DATA + } +} +chassis { + aggregated-devices { + ethernet { + device-count 1; + } + } +} +interfaces { + ge-0/0/0 { + description ae0; + ether-options { + 802.3ad ae0; + } + } + ge-0/0/1 { + description ae0; + ether-options { + 802.3ad ae0; + } + } + ge-0/0/2 { + description ae0; + ether-options { + 802.3ad ae0; + } + } + ae0 { + description "Aggregation towards $distro_name $distro_phy_port"; + aggregated-ether-options { + minimum-links 2; + lacp { + active; + } + } + unit 0 { + family ethernet-switching { + port-mode trunk; + vlan { + members [ deltagere mgmt ]; + } + } + } + } + vlan { + unit $mgmt_vlan { + description "Management L3 interface"; + family inet { + address $mgmt_addr/$mgmt_cidr; + } + } + } +} +vlans { + deltagere { + vlan-id 200; + } + mgmt { + vlan-id $mgmt_vlan; + l3-interface vlan.$mgmt_vlan; + } +} + +routing-options { + static { + route 0.0.0.0/0 next-hop $mgmt_gw; + } +} diff --git a/junos-bootstrap/httpd/postgres_queries b/junos-bootstrap/httpd/postgres_queries new file mode 100644 index 0000000..d7c07f2 --- /dev/null +++ b/junos-bootstrap/httpd/postgres_queries @@ -0,0 +1,20 @@ +CREATE TABLE switches ( + id serial primary key, + hostname varchar(20) NOT NULL, + distro_name varchar(100) NOT NULL, + distro_phy_port varchar(100) NOT NULL, + mgmt_addr varchar(15) NOT NULL, + mgmt_cidr smallint NOT NULL, + mgmt_gw varchar(15) NOT NULL, + mgmt_vlan smallint NOT NULL, + last_config_fetch integer default NULL, + current_mac varchar(17) default NULL +); + + + +insert into switches (hostname, distro_name, distro_phy_port, mgmt_addr, mgmt_cidr, mgmt_gw, mgmt_vlan) values +('e-00-0-test', 'distro-test', 'ge-0/0/0', '10.0.200.2', '24', '10.0.200.1', '300'), +('e-00-1-test', 'distro-test', 'ge-0/0/3', '10.0.200.3', '24', '10.0.200.1', '300'), +('e-00-2-test', 'distro-test', 'ge-0/0/6', '10.0.200.4', '24', '10.0.200.1', '300'), +('e-60-0-test', 'distro-test', 'ge-0/0/9', '10.0.200.5', '24', '10.0.200.1', '300'); diff --git a/junos-bootstrap/httpd/server_http.py b/junos-bootstrap/httpd/server_http.py new file mode 100644 index 0000000..20e5de5 --- /dev/null +++ b/junos-bootstrap/httpd/server_http.py @@ -0,0 +1,110 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +from http.server import BaseHTTPRequestHandler, HTTPServer +from string import Template +import time +import psycopg2 +import psycopg2.extras +import sys + +def main(): + # + # Settings + # + settings = dict( + db = dict( + user = 'bootstrap', + password = 'asdf', + dbname = 'bootstrap', + host = 'localhost' + ), + http = dict( + host = 'localhost', + port = 9000 + ) + ) + + # + # Connect to DB + # + try: + connect_params = ("dbname='%s' user='%s' host='%s' password='%s'" % (settings['db']['dbname'], settings['db']['user'], settings['db']['host'], settings['db']['password'])) + conn = psycopg2.connect(connect_params) + # cur = conn.cursor() + cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor) + cur.execute("""SELECT * from switches""") + rows = cur.fetchall() + print ("\nSwitches in DB during server_http.py startup:") + for row in rows: + print (" --> %s, connected to %s port %s" % (row['hostname'], row['distro_name'], row['distro_phy_port'])) + + except (psycopg2.DatabaseError, psycopg2.OperationalError) as e: + print ('Error: %s' % e) + sys.exit(1) + + except: + print(sys.exc_info()[0]) + sys.exit(1) + + def template_get(model): + return open(model + '.template').read() + + def template_parse(template_src, hostname): + cur.execute("SELECT * FROM switches WHERE hostname = '%s'" % hostname) + if(cur.rowcount == 1): + row = cur.fetchall()[0] + print(' --> DB response ok, populating template') + d={ + 'hostname': row['hostname'], + 'distro_name': row['distro_name'], + 'distro_phy_port': row['distro_phy_port'], + 'mgmt_addr': row['mgmt_addr'], + 'mgmt_cidr': row['mgmt_cidr'], + 'mgmt_gw': row['mgmt_gw'], + 'mgmt_vlan': row['mgmt_vlan'] + } + return Template(template_src).safe_substitute(d) + else: + print(' --> No hits in DB for hostname "%s", cannot continue' % hostname) + return False + + class httpd(BaseHTTPRequestHandler): + def do_GET(self): + print('[%s] Incoming request: source:%s path:%s ' % (time.asctime(), self.client_address[0], self.path)) + if '/tg15-edge/' in self.path: + hostname = self.path.split('/tg15-edge/')[1] + if len(hostname) > 0: + print(' --> hostname "%s" accepted, fetching info from DB' % hostname) + template_parsed = template_parse(template_get('ex2200'), hostname) + if template_parsed: + print(' --> sending response to client') + self.send_response(200) + self.send_header("Content-type", "text/plain") + self.end_headers() + self.wfile.write(bytes(template_parsed, "utf-8")) + print(' --> success - %s bytes sent to client' % len(template_parsed)) + else: + print(' --> error - template could not be populated') + else: + print(' --> rejected due to missing hostname') + else: + print(' --> rejected due to bad path') + # silence stderr from BaseHTTPRequestHandler + # source: http://stackoverflow.com/questions/3389305/how-to-silent-quiet-httpserver-and-basichttprequesthandlers-stderr-output + def log_message(self, format, *args): + return + + httpd_instance = HTTPServer((settings['http']['host'], settings['http']['port']), httpd) + print("\n[%s] Server Starts - %s:%s" % (time.asctime(), settings['http']['host'], settings['http']['port'])) + + try: + httpd_instance.serve_forever() + except KeyboardInterrupt: + pass + + httpd_instance.server_close() + print("\n\n[%s] HTTP Server stopped\n" % time.asctime()) + +if __name__ == "__main__": + main() diff --git a/junos-bootstrap/httpd/terminal.log b/junos-bootstrap/httpd/terminal.log new file mode 100644 index 0000000..1974079 --- /dev/null +++ b/junos-bootstrap/httpd/terminal.log @@ -0,0 +1,34 @@ +j@lappie:~/dhcp-tech82$ python3 server_http.py + +Switches in DB during server_http.py startup: + --> e-00-0-test, connected to distro-test port ge-0/0/0 + --> e-00-1-test, connected to distro-test port ge-0/0/3 + --> e-00-2-test, connected to distro-test port ge-0/0/6 + --> e-60-0-test, connected to distro-test port ge-0/0/9 + +[Wed Jan 28 00:38:18 2015] Server Starts - localhost:9000 +[Wed Jan 28 00:38:42 2015] Incoming request: source:127.0.0.1 path:/tg15-edge/e-00-0-test + --> hostname "e-00-0-test" accepted, fetching info from DB + --> DB response ok, populating template + --> sending response to client + --> success - 1442 bytes sent to client +[Wed Jan 28 00:38:50 2015] Incoming request: source:127.0.0.1 path:/tg15-edge/e-00-1-test + --> hostname "e-00-1-test" accepted, fetching info from DB + --> DB response ok, populating template + --> sending response to client + --> success - 1442 bytes sent to client +[Wed Jan 28 00:38:59 2015] Incoming request: source:127.0.0.1 path:/tg15-edge/e-00-5-test + --> hostname "e-00-5-test" accepted, fetching info from DB + --> No hits in DB for hostname "e-00-5-test", cannot continue + --> error - template could not be populated +[Wed Jan 28 00:39:27 2015] Incoming request: source:127.0.0.1 path:/tg15-edge/e-60-0-test + --> hostname "e-60-0-test" accepted, fetching info from DB + --> DB response ok, populating template + --> sending response to client + --> success - 1442 bytes sent to client +^C + +[Wed Jan 28 00:39:34 2015] HTTP Server stopped + +j@lappie:~/dhcp-tech82$ + diff --git a/junos-bootstrap/proof_of_concepts/tg15-tech82-poc1.tar.gz b/junos-bootstrap/proof_of_concepts/tg15-tech82-poc1.tar.gz Binary files differnew file mode 100644 index 0000000..2844b59 --- /dev/null +++ b/junos-bootstrap/proof_of_concepts/tg15-tech82-poc1.tar.gz |