aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--junos-bootstrap/README.md8
-rw-r--r--junos-bootstrap/httpd/ex2200.template73
-rw-r--r--junos-bootstrap/httpd/postgres_queries20
-rw-r--r--junos-bootstrap/httpd/server_http.py110
-rw-r--r--junos-bootstrap/httpd/terminal.log34
-rw-r--r--junos-bootstrap/proof_of_concepts/tg15-tech82-poc1.tar.gzbin0 -> 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
new file mode 100644
index 0000000..2844b59
--- /dev/null
+++ b/junos-bootstrap/proof_of_concepts/tg15-tech82-poc1.tar.gz
Binary files differ