Convert csv to switch commands Python The script should be a single python file that takes an excel file (or csv).  Each row represents one switch.  For each switch, there will be one or more interfaces.  The objective is to output a script of commands to execute to configure each switch, as specified.

For example, Row 1 is below:

switch1    Eth1    trunk    10,20,30    Eth2    Access    10    Y


The script should see that there are two interfaces.  For each trunk interface, the script should output the command “swithport mode trunk”, then “switchport trunk allow vlan …” with the list of allowed VLANs.  For each access interface, it’s the same, but with the word “access” instead of “trunk”.  

The sample output for the above row is as below;

switch1

hostname switch1
!
interface Eth1
 switchport mode trunk
 switchport trunk allow vlan 10,20,30
!
interface Eth2
 switchport mode access
 switchport access vlan 10


Each switch’s output should be written as a separate column in a new csv or excel file. 

My submitted code as below, please review and guide me for improvements;

#!/usr/bin/python
import csv
import sys
import re
import argparse


def parseCSV(path):
    '''
    Parses a CSV file and return the rows as a list
    '''
    rows = []
    try:
        f = path
    except IOError:
        print "Cannot open specified file: %s" % path
    else:
        reader = csv.reader(f)
        for row in reader:
            rows.append(row)
        f.close()
    return rows


def outputCSV(destpath, rows):
    '''
    Process the input rows and provide switch command output.
    Output then processed to destination CSV file.
    '''
    try:
        f = destpath
    except IOError:
        print "Cannot open specified file: %s" % destpath
    else:
        writer = csv.writer(f)
    for i in range(len(rows)):
        new_list = []
        final_string = ''
        if re.match(r'^switch\d$', rows[i][0]):
            new_list.append(rows[i][0])
            new_list.append('')
            new_list.append('hostname '+rows[i][0])
            new_list.append('!')
            if rows[i][1] is not None:
                new_list.append('interface '+rows[i][1].lower())
                new_list.append(' switchport mode '+rows[i][2])
                new_list.append(' switchport '+rows[i][2]+' allow vlan '+rows[i][3])
            new_list.append('!')
            if rows[i][4] is not None:
                new_list.append('interface '+rows[i][4].lower())
                new_list.append(' switchport mode '+rows[i][5].lower())
                new_list.append(' switchport '+rows[i][5].lower()+' vlan '+rows[i][6])
            final_string = '\n'.join(new_list)
            writer.writerow([final_string])
    f.close()


def main(args):
    '''
    Parses the command line arguments
    Arguments are:
        arg1: Input CSV file to process
        arg2: Output CSV file 

    Sample program execution: python myfile.py arg1 arg2
    '''
    parser = argparse.ArgumentParser(description="Reads CSV input and provide switch commands in CSV output file")
    parser.add_argument('path', help="CSV file to process", type=argparse.FileType('rt'))
    parser.add_argument('destpath', help="Output CSV file", type=argparse.FileType('at'))
    try:
        args = parser.parse_args()
    except IOError, msg:
        parser.error(str(msg))
        return
    rows = parseCSV(args.path)
    outputCSV(args.destpath, rows)


if __name__ == '__main__':
    args = sys.argv[1:]
    main(args)
First, your exception wrappers are useless:


try:
    f = destpath
except IOError:
    print "Cannot open specified file: %s" % destpath


destpath is already a file object. If there were an opening problem, then argparse would have told you with an exception at this level.


  parseCSV function can be rewritten in one line by turning the csv reader object to a list by iterating on it when building the list object:


def parseCSV(path):
    '''
    Parses a CSV file and return the rows as a list
    '''
    return list(csv.reader(path))


(I suggest that you close the file outside the parseCSV method as it wasn't its responsibility to open the file in the first place)


  outputCSV function:


don't use the indices here, iterate on the elements:

for i in range(len(rows)):


becomes:

for row in rows:  # "rows[i]" becomes "row"


now all those append statements can be simplified/shortened by passing lists to extend.

Also note that if rows[i][1] is not None is always True: csv module returns str objects, never None.

    new_list = []
    if re.match(r'switch\d$', rows[i][0]):  # no need for ^: it's match not search
        new_list.extend([row[0],'','hostname '+row[0],'!'])
        # skipping the "always true" condition, you may need to adapt this
        new_list.extend(['interface '+row[1].lower(),' switchport mode '+row[2],' switchport '+row[2]+' allow vlan '+row[3],'!'])
        # skipping the "always true" condition, you may need to adapt this                
        new_list.extend(['interface '+row[4].lower,' switchport mode '+row[5].lower(),' switchport '+row[5].lower()+' vlan '+rows[i][6]])
        writer.writerow(['\n'.join(new_list)])


Last but not least, when using csv &amp; text mode in Windows you stumble on the


  infamous blank-line-every-two-line bug ¹ ² ³


So for python 2 (seems to be the case), you may want to change the argparse for output to:

parser.add_argument('destpath', help="Output CSV file", type=argparse.FileType('ab'))  # open as binary


and for python 3:

parser.add_argument('destpath', help="Output CSV file", type=argparse.FileType('at',newline="")) # don't issue extra CR chars, well, some magic like that :)