#!/usr/bin/env python # -*- coding: utf-8 -*- # # netmap-csv.py # # Copyright 2015 Neil Williams # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301, USA. # # import os import sys import csv import glob import yaml import argparse def main(args): """ Two different formats are needed. The network dict reflects how the XMLRPC will output the data and is a closer match to the spreadsheet so that updates are easier to check. The devices dict is the output equivalent to the device dictionary import, output as device-specific Jinja2 files. e.g. {% extends 'vland.yaml' %} {% set interfaces = ['eth0', 'eth1'] %} {% set sysfs = { 'eth0': "/sys/devices/pci0000:00/0000:00:19.0/net/eth0", 'eth1': "/sys/devices/pci0000:00/0000:00:1c.1/0000:03:00.0/net/eth1"} %} {% set mac_addr = {'eth0': "f0:de:f1:46:8c:21", 'eth1': "00:24:d7:9b:c0:8c"} %} {% set tags = {'eth0': ['1G', '10G'], 'eth1': ['1G']} %} {% set map = {'eth0': {'192.168.0.2': 5}, 'eth1': {'192.168.0.2': 7}} %} All output occurs in the ./output/ directory. """ parser = argparse.ArgumentParser(description='network map CSV import') parser.add_argument( '--input', metavar='FILE', type=str, dest='filename', required=True, help='CSV file') args = parser.parse_args() if not args.filename: parser.print_help() return 0 rows = [] network = {} devices = {} errors = [] if not os.path.exists('./output'): os.mkdir('output') with open(args.filename, 'r') as csvfile: content = csv.DictReader(csvfile) for row in content: rows.append(row) for row in rows: for key, value in row.items(): if key == 'Switch Address' and value and 'Switch Port' in row: try: int(row['Switch Port']) except (ValueError, TypeError): print >> sys.stderr, "Failed to parse 'Switch Port': '%s' as a number" % row['Switch Port'] print >> sys.stderr, "Skipping %s" % row['Device'] errors.append(row) break network.setdefault('switches', {}) network['switches'].setdefault(str(value), []) data = {'port': row['Switch Port']} data['device'] = { 'interface': row['Device eth port'], 'mac': row['MAC Addr'], 'sysfs': row['sysfs path'], 'hostname': row['Device'].replace('*', '') } network['switches'][str(value)].append(data) elif key == 'Device' and value: try: int(row['Switch Port']) except (ValueError, TypeError): print >> sys.stderr, "Failed to parse 'Switch Port': '%s' as a number" % row['Switch Port'] print >> sys.stderr, "Skipping %s" % row['Device'] errors.append(row) break name = row['Device'].replace('*', '') devices.setdefault(name, {}) devices[name]['extends'] = '' # needs to be Device Type devices[name].setdefault('interfaces', []) iface = row['Device eth port'] devices[name]['interfaces'].append(iface) devices[name].setdefault('sysfs', {}) devices[name]['sysfs'][iface] = row['sysfs path'] devices[name].setdefault('mac_addr', {}) devices[name]['mac_addr'][iface] = row['MAC Addr'] devices[name].setdefault('tags', {}) devices[name]['tags'][iface] = row['Link Speeds'].split(', ') devices[name].setdefault('map', {}) devices[name]['map'].setdefault(iface, {}) devices[name]['map'][iface].update({ row['Switch Address']: int(row['Switch Port']) }) if devices: outputs = glob.glob('./output/*.jinja2') for stale in outputs: os.unlink(stale) for key, value in devices.items(): with open('./output/%s.jinja2' % key, 'w') as device_output: device_output.write("{%% extends '%s' %%}\n" % value['extends']) device_output.write("{%% set interfaces = %s %%}\n" % value['interfaces']) device_output.write("{%% set sysfs = %s %%}\n" % value['sysfs']) device_output.write("{%% set mac_addr = %s %%}\n" % value['mac_addr']) device_output.write("{%% set tags = %s %%}\n" % value['tags']) device_output.write("{%% set map = %s %%}\n" % value['map']) with open('./output/output.yaml', 'w') as output: yaml.dump(network, output, default_flow_style=False) if errors: print "There were errors!" return 1 return 0 if __name__ == '__main__': import sys sys.exit(main(sys.argv))