def main():
    args = setup_steps()
    ip = args.host
    interface = args.interface
    cost = args.cost
    state = add_cost_workflow(ip, interface, cost, "CLI")
    print_error("finished add cost workflow")
Exemplo n.º 2
0
def verify_arguments(arguments):
    """
    Author - Jonathan Steward
    Function - asks user for verification before carrying out any external calls
    Inputs - argparse object 
    returns - bool - determines the state of if the arguments
    """
    confirm_statement = """
    Please confirm the following settings:
    Adding to lag?: {addremove}
    Device: {ip}
    Interface: {int}
    Agg port: {ae}
    If you are adding an interface to a group, please ensure the group port exists
    """
    print confirm_statement.format(addremove=arguments.activate,
                                   ip=arguments.ip,
                                   int=arguments.interface,
                                   ae=arguments.ae_int)

    confirmed = ""
    while confirmed != "y":
        confirmed = raw_input("y/n:")
        confirmed = confirmed.lower()
        if confirmed == "n":
            print_error("quitting due to user inputs")
            return False
        elif confirmed == "y":
            user = getpass.getuser()
            print "user {} confirmed".format(user)
        else:
            print_error("please a single y or n")
    return True
def interface_down_check(message, ip):
    """
    Author - Jonathan Steward
    Function - workflow function for calling the relevent checks and then automation
    Inputs -
        message - string - syslog message 
        ip - string - ip address of sending device
    returns -
        bool - state of if it matched the regex
        string - the issue with the message, if matched this will detail the ip address of device
                 and the ip of the device with the interface down.
    """

    state, int_down = message_check(message)

    if not state:
        #print_error("The following message didn't match the regex needed  so no interface went physically down\n {}".format(message))
        return (False, "", False)

    print_error(message)

    issue = "{} interface on {} is down".format(int_down, ip)

    state, device = device_config_info_lookup(ip)

    if not state:
        # no data/too much data.
        return (True, issue, False)

    return_message, acted = process_down(int_down, device)
    issue = "{} {}".format(issue, return_message)
    return True, issue, acted
Exemplo n.º 4
0
def identify_device_RID_from_int_ip(ip):
    """
    Author - Jonathan Steward
    Function - finds the device_id based on interface ip
    Inputs - String - Ip address of interface
    returns - int - ID of related device
    """
    command = """
    SELECT device_id from `FYP Data`.interfaces
    WHERE ip_address = '{}';""".format(ip)

    result = get_data(command)
    print "device_id for {} ip is {}".format(ip, result)
    if not result:
        print_error("something wrong getting device information")
        return (False, "")

    if len(result) > 1:
        print_error("more than one device record, can't carry out automation")
        # email admin
        return (False, "")

    result = result[0][0]
    print "device id is {}".format(result)
    command = """
    SELECT ip from `FYP Data`.device_table
    WHERE device_id = '{}';""".format(result)

    result = get_data(command)

    print "device RID is {}".format(result[0][0])

    return result[0][0]
Exemplo n.º 5
0
def main():

    arguments = setup_steps()
    if not arguments:
        print_error("Invalid arguments")
        return

    state, device = device_config_info_lookup(arguments.ip)
    # device vendor,community,username,loginpassword,enpassword
    if not state:
        return

    if device.vendor == "cisco":
        arguments.ae_int = "{}{}".format("Port-channel", arguments.ae_int)
    elif device.vendor == "juniper":
        arguments.ae_int = "{}{}".format("ae", arguments.ae_int)
    else:
        print_error("The vendor '{}' isn't valid please re-try".format(
            arguments.vendor))
        return
    state = verify_arguments(arguments)
    if state:
        process_lag_change(arguments.activate, arguments.ip, device.username,
                           device.password, device.vendor, arguments.interface,
                           arguments.ae_int, device.community,
                           device.enablePassword)
Exemplo n.º 6
0
def find_ae_int_ref(ip, ae_int, interface, community, activate):
    """
    Author - Jonathan Steward
    Function - identify the lag and interface ifindex and all interfaces
    Inputs - 
        ip - string
        ae_int - string
        interface - string
        community - string
    returns - 
        bool - state of if interface and lag found
        int - ae_index
        int - interface_index
        list - device_interfaces
    """
    interface_unit = interface + ".0"

    device_interfaces = Grab_device_interfaces_snmp(ip, community)

    ae_index = ""
    interface_index_unit = ""
    interface_index = ""
    print "checking for ae {}".format(ae_int)

    for inter in device_interfaces:
        if ae_int:
            if str(inter.value) == str(ae_int):
                ae_index = inter.oid_index
                print "found matching AE index from list of interfaces"
                print ae_index
                continue

        if str(inter.value) == str(interface_unit):
            interface_index_unit = inter.oid_index
            print "found interface_unit index"
            print interface_index_unit
            continue
        if str(inter.value) == str(interface):
            interface_index = inter.oid_index
            print "found interface index"
            print interface_index
            continue

    if not interface_index:
        print_error(
            "requested interface {} not found at all, please re-check".format(
                interface))
        return False, "", "", ""
    if interface_index_unit:
        interface_index = interface_index_unit

    if not ae_index:
        print_error("{} not found on device".format(ae_int))
        if activate:
            return False, "bad", "", ""

    return True, ae_index, interface_index, device_interfaces
Exemplo n.º 7
0
def parse(message):
    """
    Author - Jonathan Steward
    Function - Function called from the monitor calls all other functions.
    Inputs - message - string
    """
    print "recieved message:\n {}".format(message)
    device_RID, issue, acted = parse_message(message)
    print_error("{}, {}\n Was it acted on: {}".format(device_RID, issue,
                                                      acted))
    record_actions(device_RID, message, issue, acted)
Exemplo n.º 8
0
def change_int_lag_state_cisco(addornot, interface, device_details, ae_int):
    """
    Author - Jonathan Steward
    Function - Configure cisco device to either add or remove interface from group
    Inputs - 
        addornot - bool
        interface - string
        device_details - object
        ae_int - int 
    returns - bool - state of if the config was applied or not.
    """
    if not ae_int:
        pass
    else:
        ae_int_number = ae_int.split("Port-channel")[1]

    connection = ssh_connection(device_details)
    if not connection:
        return False

    command = ("\nen\n{}\nconfig t\n\n\n\n\n".format(
        device_details.enablePassword))
    connection.send(command)
    time.sleep(0.5)
    output = connection.recv(8000)
    if "#" not in output:
        print_error(
            "password was wrong!, see below for the issue:\n{}".format(output))
        return False
    print output

    if addornot:
        lagcommand = "\ninterface {}\n channel-group {} mode active\n".format(
            interface, ae_int_number)
    else:
        lagcommand = "\ninterface {}\nno channel-group\n".format(interface)

    connection.send(lagcommand)
    time.sleep(1)

    output = connection.recv(8000)

    if "% Invalid arguments detected" in output:
        print_error("something went wrong, see below:{}".format(output))

    print "applied config as seen below:\n{}".format(output)

    command = "\nend\ncopy run start\n"
    connection.send(command)
    time.sleep(1)

    return True
def Juniper_config(configuration, device_object, config_format='xml'):
    """
    Author - Jonathan Steward
    Function - use device connection object and connect to load configuration
    Inputs -
        configuration - string of what configuration you want to apply
        device_object - device object containing all the device
        config_format - the format of the configuration, default is xml but can also be set commands
    returns - bool - state of if the config operation worked or not.
    """
    device_con = Juniper_connect(device_object)
    if not device_con:
        return False
    try:
        with Config(device_con, mode='private') as connection:
            print "{}: connected, loading".format(
                time.strftime('%Y-%m-%d %H:%M:%S'))
            try:
                connection.load(configuration,
                                format=config_format,
                                overwrite=True)
            except ConfigLoadError as error:
                print_error(
                    "there was an issue loading configuration:\n{}".format(
                        error))
                return False
            except XMLSyntaxError as error:
                print_error("there was a syntax error:\n{}".format(error))
                return False
            except:
                print_error("Something went wrong".format(error))
                return False

            print "{}: loaded, commiting the following change:".format(
                time.strftime('%Y-%m-%d %H:%M:%S'))
            diff = connection.pdiff()
            print diff

            try:
                connection.commit(
                    comment='Adding description via api with xml file')
            except CommitError as error:
                print_error(
                    "There was an issue with the commit!{}".format(error))
                return False
            print "{}: commit complete".format(
                time.strftime('%Y-%m-%d %H:%M:%S'))
        device_con.close()
    except:
        print_error("Something went wrong applying config")
    return True
Exemplo n.º 10
0
def check_success(activate, int_state, interface, ae_int):
    """
    Author - Jonathan Steward
    Function - Simple function to check if an interface was added into lag and output to user/logs
    Inputs - 
        activate - bool
        int_state - bool
        interface - string
        ae_int - int 
    """

    if activate:
        if int_state:
            # trying to add and also in lag
            # success
            print_error("{} was added to {}".format(interface, ae_int))
        else:
            # trying to add but not in lag
            # fail
            print_error(
                "{} was not added to {} for some reason, might be down other end"
                .format(interface, ae_int))
    else:
        if int_state:
            # Trying to remove and in lag
            # fail
            print_error("{} was not removed from {} for some reason".format(
                interface, ae_int))
        else:
            print_error("{} was removed from lag {}".format(interface, ae_int))
Exemplo n.º 11
0
def poll_traffic(device_interfaces, device_ip, community):
    """
    Author - Jonathan Steward
    Function - Polls Device for interface counter and then calls traffic automation for loops
    Inputs -
        device_interfaces - list - list of interface objects from snmp gets with database info
        device_ip - string - ip address of host with high utilization
        community - string - community string needed for SNMP
    returns - n/a
    """
    interfaces_traffic = snmppoll("WALK", ".1.3.6.1.2.1.31.1.1.1.10", device_ip, community)
    time_now = datetime.now()
    print "polled interface traffic on {}".format(device_ip)
    if not interfaces_traffic:
        print_error("no interface traffic stats reutrned!")
        return
    for int_traffic in interfaces_traffic:
        for i in range(len(device_interfaces)):
            if int(int_traffic.oid_index) != int(device_interfaces[i]["oid_index"]):
                # Not a matched interface
                continue

            if device_interfaces[i]["speed"] == 0:
                # Will always alarm no need for this
                break

            device_interfaces[i]["current_counter"] = int(int_traffic.value)

            state, device_interfaces[i] = calculate_interface_util(device_interfaces[i], time_now)
            if not state:
                break

            if device_interfaces[i]["util_percentage"] > 1:
                print_polling_traffic_stats(device_interfaces[i])

                print "threashold is {}% current usage is {}% on {} for {} device".format(
                    percentage_threashold,
                    device_interfaces[i]["util_percentage"],
                    device_interfaces[i]["name"],
                    device_ip)
            update_interface_and_history(device_interfaces[i])

            if device_interfaces[i]["util_percentage"] > percentage_threashold:
                print "interface {} on {} is at {}% which is above threashold".format(
                    device_interfaces[i]["name"],
                    device_ip,
                    device_interfaces[i]["util_percentage"])
                traffic_automation(device_interfaces[i], device_ip, community)
def setup_steps():
    """
    Author - Jonathan Steward
    Function - carry out arg parse setup taking in arguments and validating that some of them are valid
    returns - argparse object - stores all arguments input
    """
    parser = argparse.ArgumentParser()
    host_help = "Host you wish to target"
    interface_help = "interface you wish to target"
    cost_help = "cost you wish to apply, should be a whole number"


    # in a perfect world this would look up passwords username and vendor detail
    parser.add_argument("-d", "--host",
                        help=host_help, dest="host", required=True)
    parser.add_argument("-i", "--interface",
                        help=interface_help, dest="interface", required=True)
    parser.add_argument("-c", "--cost",
                        help=cost_help, dest="cost", required=True)

    # Note other community data would be needed if a more secure implementation was used.

    arguments = parser.parse_args()

    try:
        int(arguments.cost)
    except ValueError:
        print_error("cost provided isn't a whole number")
        sys.exit()

    # used to match the corect regex and grab the interface that is down

    match_interface_name = "(Ethernet|FastEthernet|GigabitEthernet|TenGigE|fe-|ge-|xe-|et-)"
    # Juniper doesn't have a standard ethernet but does have fast,gig,ten and 100 gig code prefix
    match_interface_number = "((\d+\/)+)*\d+"

    interface_regex = match_interface_name + match_interface_number
    interface_check = re.match(interface_regex, arguments.interface)
    if not interface_check:
        print_error("interface isn't valid")
        sys.exit()

    ip_state = check_ip(arguments.host)
    if not ip_state:
        sys.exit()

    return arguments
Exemplo n.º 13
0
def int_in_ae(ip, ae_int, interface, community, activate, stage):
    """
    Author - Jonathan Steward
    Function - identifies if the interface is in the lag and if so what lag.
    Inputs - 
        ip - string
        ae_int - string
        interface - string
        community - string
    returns - 
        bool - state of if the interface is in lag
        int - ae_int the interface is in.
    """
    state, ae_index, interface_index, device_interfaces = find_ae_int_ref(
        ip, ae_int, interface, community, activate)
    # print interface_index
    # print ae_index
    if ae_index == "bad":
        return False, "bad"
    if not state:
        # Error messages in find_ae_int
        return False, ""
    if not activate and stage == 1:
        if ae_int:
            return True, ae_int
        else:
            return True, ""
    result = SnmpPoll(
        'GET', '.1.2.840.10006.300.43.1.2.1.1.13.{}'.format(interface_index),
        ip, community)
    print result

    if not ae_index:
        if not result.oid_index:
            print_error("no AE interface found assigned to interface")
            if activate:
                return False, ""
        for inter in device_interfaces:
            if inter.oid_index == result.value:
                ae_int = inter.value
                print_error("Found AE port {}".format(ae_int))
                return True, ae_int
    elif result.value == ae_index:
        return True, ae_int

    return False, ae_int
def Juniper_connect(device_object):
    """
    Author - Jonathan Steward
    Function - Create a connnection to a juniper device
    Inputs - device_object - Device object
    returns - device connection object
    """
    print "{}: connecting".format(time.strftime('%Y-%m-%d %H:%M:%S'))
    try:
        device_con = Device(host=device_object.ip,
                            user=device_object.username,
                            passwd=device_object.password).open()
    except ConnectError as error:
        print_error("{}: there was an issue connecting: {}".format(
            time.strftime('%Y-%m-%d %H:%M:%S'), error))
        return
    return device_con
Exemplo n.º 15
0
def identify_ip(message):
    """
    Author - Jonathan Steward
    Function - From a messagem parse for the ip address
    Inputs - message - string
    returns - ip - string - if failed returns a blank string
    """
    ip_regex = "(?P<host>\d+\.\d+\.\d+\.\d+).*"
    ip = re.match(ip_regex, message)
    if not ip:
        print_error("Syslog message not valid!: \n{}".format(message))
        return ""
    ip = ip.group("host")
    ip_state = check_ip(ip)
    if not ip_state:
        return ""
    return ip
def add_cost_workflow(ip, interface, cost, called_from):
    """
    Author - Jonathan Steward
    Function - Workflow function calling relevant functions needed for configuration
    Inputs -
        ip - string - ip address of host on which cost needs to be applied
        interface - string - the interface name of the interface to apply cost to
        cost - int - Cost to apply to the interface
        called_from - string - what is callling this fuction for logging
    returns -
        bool - state of the workflow
    """
    arg_string = "host : {}, interface: {}, cost: {}, called from : {}".format(
        ip,
        interface,
        cost,
        called_from)
    tool = ToolLog("deprefer tool", arg_string)

    state, device = device_config_info_lookup(ip)
    if not state:
        print "didn't get device_config info"
        tool.set_tool_log(False)
        return False
    config_state = False

    print "starting add_cost_workflow"
    if device.vendor == "cisco":
        lock_state = get_config_lock(device.ip, "deprefer tool")
        if lock_state:
            config_state = add_cost_interface_cisco_ospf(device, interface, cost)
            print_error("attempted to configured cisco device")
    elif device.vendor == "juniper":
        lock_state = get_config_lock(device.ip, "deprefer tool")
        if lock_state:
            config_state = add_cost_interface_juniper_ospf(device, interface, cost)
            print_error("attempted to configured juniper device")
    else:
        print "unsupported vendor {}".format(device.vendor)
        tool.set_tool_log(False)
        return False

    set_config_lock(device.ip, False, "deprefer tool")
    print"config lock released"
    tool.set_tool_log(config_state)
    return True
def add_cost_interface_cisco_ospf(device, interface, cost):
    """
    Author - Jonathan Steward
    Function - Sends the relevant commmands to a cisco device to apply ospf cost to interface
    Inputs -
        device - device object - stores relevant details of device for configuration
        interface - string - interface that we are adding cost to
        cost - int - cost to be applied to interface
    returns -
        bool - state of the success of configuration
    """
    connection = ssh_connection(device)
    if not connection:
        return False

    # Attempt to log into config
    command = ("\nen\n{}\nconfig t\n".format(device.enablePassword))
    connection.send(command)
    time.sleep(0.5)
    output = connection.recv(8000)
    if "#" not in output:
        print_error("password was wrong!, see below for the issue:\n{}".format(output))
        return False
    print output

    # Attempt to get to interface prompt
    command = "\ninterface {interface}\n".format(interface=interface)
    connection.send(command)
    time.sleep(0.5)
    output = connection.recv(8000)
    if "(config-if)" not in output:
        print_error("interface {} was wrong, see below for output:\n{}".format(interface, output))
        return False
    print output

    # Attempt to apply to cost
    command = "\nip ospf cost {cost}\n".format(cost=cost)
    connection.send(command)
    time.sleep(0.5)
    output = connection.recv(8000)
    if "% Invalid" in output:
        print_error("something went wrong, might not be a L3 interface:{}".format(output))
        return False
    print output
    
    command = "\nend\ncopy run start\n"
    connection.send(command)
    time.sleep(1)

    print_error("configured cost correctly")
    return True
def ssh_connection(device):
    """
    Author - Jonathan Steward
    Function - create an SSH connection
    Inputs - device - device object - stores device details for connecting 
    returns - remote_con - paramiko shell object
    """
    remote_con_pre = paramiko.SSHClient()
    remote_con_pre.set_missing_host_key_policy(paramiko.AutoAddPolicy())

    try:
        remote_con_pre.connect(device.ip,
                               username=device.username,
                               password=device.password,
                               look_for_keys=False,
                               allow_agent=False)
    except IOError as error:
        print_error(
            "Can't connect to SSH for following reason:\n{}".format(error))
        return
    except paramiko.PasswordRequiredException as error:
        print_error("Username/Password incorrect")
        return
    except paramiko.AuthenticationException as error:
        print_error("Password/Username incorrect")
        return

    remote_con = remote_con_pre.invoke_shell()
    remote_con.settimeout(10)
    return remote_con
def find_device_ip(device_id):
    """
    Author - Jonathan Steward
    Function - find the RID of the device required along with some other details 
    Inputs - int - device_id - device_id of the request device
    returns -
        string - RID of device
        string - Vendor of device
        int - ASN of device
    """
    command = """
    SELECT ip, vendor, asn from `FYP Data`.device_table
    WHERE device_id = {}
    """.format(device_id)
    data = get_data(command)
    if len(data) > 1:
        print_error("There multipul devices listed under device_id {} somehow".format(device_id))
        return "", "", ""
    if len(data) == 0:
        print_error("No Data was found for device_id {}".format(device_id))
        return "", "", ""
    print data[0]
    return data[0][0], data[0][1], data[0][2]
Exemplo n.º 20
0
def record_actions(host, message, issue, acted):
    """
    Author - Jonathan Steward
    Function - sends details to db about event and what happened
    Inputs -
        host - string
        message - string
        issue - string
        acted - bool
    """
    device_id = get_device_id(host)
    command = """
    INSERT INTO `FYP Data`.`syslog_events` (`syslog detail`, `device_id`, `acted_on`, `message`)
     VALUES ("{}", "{}", "{}", "{}");
    """.format(issue, device_id, acted, message)
    if "License" in message:
        pass
    elif "bgp" in message:
        pass
    else:
        print_error(
            "sending following command to log event\n{}".format(command))

    set_data_mysql(command)
def process_down(int_down, device):
    """
    Author - Jonathan Steward
    Function - Call function and evaluate when trying to remove interface from lag
    Inputs -
        int_down - string - interface name that went down
        device - device object storing all details
    returns -
        string - the return message to state what happened
        bool - State of the automation working or not
    """

    print "taking {} out of lag on {}".format(int_down, device.ip)

    state = process_lag_change(False, device.ip, device.username,
                               device.password, device.vendor, int_down, "",
                               device.community, device.enablePassword)
    if not state:
        print_error("Didn't remove interface from lag see above for logs")
        # email admin
        return "Didn't remove interface from lag because of issue raised within automation script", False

    print "message was indicating that interface was down, automation triggered"
    return ("int {} down removed from lag".format(int_down)), True
Exemplo n.º 22
0
def check_for_event(device_int, device_ip):
    """
    Author - Jonathan Steward
    Function - checks for an existing event and if one exists if its an old event or not
    Inputs - 
        Global - timeout_minuets - defined at the top to identify how many minuets old an event
                                   needs to be before closing it.
        device_int - object - combined database and snmp gathered information
        device_ip - string - ip address of the host for this event
    returns - 
    """
    command = """
    SELECT * FROM `FYP Data`.interface_events
    where `interface_id` = '{}'
    and `state` = 'active'
    and `issue` = 'out utilization'""".format(device_int["db_id"])
    events = get_data(command)
    if events:
        time_now = datetime.now()
        time_diff = (time_now - events[0][4]).seconds
        timeout_minuets = 5
        if time_diff / 60 > timeout_minuets:
            print_error("closing old event older than {} minuets".format(timeout_minuets))
            command = """
            UPDATE `FYP Data`.`interface_events`
            SET `state` = 'resolved'
            WHERE event_id = {} ;""".format(events[0][0])
            set_data_mysql(command)
        else:
            print "event for {} on {} already exists will not act".format(device_int["name"], device_ip)
            return False
    command = """
    INSERT INTO `FYP Data`.`interface_events` (`interface_id`, `state`, `issue`)
    VALUES ('{}', 'active', 'out utilization');""".format(device_int["db_id"])
    set_data_mysql(command)
    return True
Exemplo n.º 23
0
def setup_steps():
    """
    Author - Jonathan Steward
    Function - carry out arg parse setup taking in arguments and validating that some of them are valid
    returns - argparse object - Stores all arguments given to the code
    """
    parser = argparse.ArgumentParser()
    activate_help = "use this to activate the lag, default is to deactivate"
    host_help = "use this to enter the host details"
    # NOTE a company would possibly have their own password/auth system with creds for automation
    # this isn't being emulated as its just proof of concept
    interface_help = "enter the full interface name as shown under show interface CASE SENSITIVE"
    aggport_help = "enter the number of the AE or Port-channel group, scrip will complete based on vendor"

    # in a perfect world this would look up passwords username and vendor detail
    parser.add_argument("-a",
                        "--activate",
                        help=activate_help,
                        dest='activate',
                        default=False,
                        action='store_true')
    parser.add_argument("-d",
                        "--host",
                        help=host_help,
                        dest="ip",
                        required=True)
    parser.add_argument("-i",
                        "--interface",
                        help=interface_help,
                        dest="interface",
                        required=True)
    parser.add_argument("-g",
                        "--aggport",
                        help=aggport_help,
                        dest="ae_int",
                        default="")

    arguments = parser.parse_args()
    activate = arguments.activate
    if not arguments.ae_int:
        if activate:
            print_error(
                "Trying to add interface but no AE port provided this isn't possible"
            )
            return
        print_error("No AE given will attmept to find the AE to remove")
    else:
        try:
            arguments.ae_int = int(arguments.ae_int)
        except ValueError:
            print_error(
                "Please input agg port as a number not full AE or Port-channel \nUsing -g --aggport you inputted {}"
                .format(arguments.ae_int))
            return

    ip_state = check_ip(arguments.ip)
    if not ip_state:
        return

    match_interface_name = "(Ethernet|FastEthernet|GigabitEthernet|TenGigE|fe-|ge-|xe-|et-)"
    int_state = re.match(match_interface_name, arguments.interface)
    if not int_state:
        print_error(
            "Interface {} is not valid, please enter the full name. Cisco is case SENSITIVE"
            .format(arguments.interface))
        return

    return arguments
Exemplo n.º 24
0
def process_lag_change(
    activate,
    ip,
    username,
    password,
    vendor,
    interface,
    ae_int,
    community,
    enpassword="******",
):
    """
    Author - Jonathan Steward
    Function - main workflow of the process to remove interface from lag on device
    Inputs - 
        activate - bool - identifies if the interface should be added or removed
        ip - string - ip address of the host 
        username - string - username needed to log into the device
        password - string - password needed to log into the device
        vendor - string - the vendor of the device
        interface - string - interface you want to add or remove
        ae_int - int - lag you want to remove interface from
        community - string - snmp community used for polling information from device
        enpassword - string - default to "cisco", needed to configure cisco devices
    returns - bool - determines if the automation worked correctly
    """
    variables = [
        "activate = {}".format(activate), "ip = {}".format(ip),
        "username = {}".format(username), "password = {}".format(password),
        "vendor = {}".format(vendor), "interface = {}".format(interface),
        "ae_int = {}".format(ae_int), "community = {}".format(community),
        "enpassword = {}".format(enpassword)
    ]
    print variables

    tool_log = ToolLog("lagchanger", variables)

    state = get_config_lock(ip, "lagchanger")
    if not state:
        tool_log.set_tool_log()
        return False

    device_details = Device_object(ip, username, password, enpassword,
                                   vendor.lower())

    int_state, original_ae_int = int_in_ae(device_details.ip, ae_int,
                                           interface, community, activate, 1)
    if not int_state and original_ae_int == "bad":
        print_error("intended ae not found")
        set_config_lock(ip, False, "lagchanger")
        tool_log.set_tool_log(True)
        return False

    if activate:
        if int_state:
            # trying to add and also in lag
            # pointless
            print_error("interface is already in a lag, can't configure")
            set_config_lock(ip, False, "lagchanger")
            tool_log.set_tool_log(True)
            return False
        else:
            # trying to add but not in lag
            print "trying to add as not in lag"
            if device_details.vendor == "cisco":
                state = change_int_lag_state_cisco(True, interface,
                                                   device_details, ae_int)
            elif device_details.vendor == "juniper":
                state = change_int_lag_state_juniper(True, interface,
                                                     device_details, ae_int)
    else:
        if int_state:
            # Trying to remove and in lag
            print "trying to remove as in lag"
            if device_details.vendor == "cisco":
                state = change_int_lag_state_cisco(False, interface,
                                                   device_details, ae_int)
            elif device_details.vendor == "juniper":
                state = change_int_lag_state_juniper(False, interface,
                                                     device_details, ae_int)
        else:
            # Trying to remove and not in lag
            # pointless
            if device_details.vendor == "cisco":
                print_error(
                    "interface not showing as in lag but cisco devices do this when they are phy down"
                )
                state = change_int_lag_state_cisco(False, interface,
                                                   device_details, ae_int)
            else:
                print_error(
                    "interface is already out of lag, no need to remove further"
                )
                set_config_lock(ip, False, "lagchanger")
                tool_log.set_tool_log(True)
                return False

    if not state:
        print_error("something failed while configuring, quitting")
        set_config_lock(ip, False, "lagchanger")
        tool_log.set_tool_log()
        return False

    print_error(
        "config should have been applied, waiting some time to ensure connection is stable"
    )
    time.sleep(30)
    int_state, ae_int = int_in_ae(device_details.ip, ae_int, interface,
                                  community, activate, 2)

    check_success(activate, int_state, interface, ae_int)

    set_config_lock(ip, False, "lagchanger")
    tool_log.set_tool_log(True)
    return True
def check_for_peers():
    """
    Author - Jonathan Steward
    Function - Gets data from the database about the different peers 
               checks the state of the peers and calls the relevant functions
    Inputs - n/a 
    returns - 
        int - configures - Number of peers configured used for logging
        int - checks - Number of peers checked used for logging
        int - fails - Number of peer actions that failed used for logging 
    """
    command = "SELECT * FROM `FYP Data`.BGP_peers"
    data = get_data(command)
    # data = peerId/device_id/remote_asn/loopback_source/remote_ip/source_interface/approved/approver/configured
    non_approved = []
    configures = []
    checks = []
    fails = []
    for peer in data:
        if peer[6] == 0:  # Need to approve
            print_error("peer id {} for device {} and remote ip {} needs to be approved".format(
                peer[0], peer[1], peer[4]))
            non_approved.append(peer[0])
        elif peer[8] == 0:  # Need to configure
            set_peer_status(peer[0], 1)
            configures.append(peer[0])
            state, peer_object, device = configure_peer_workflow(peer)
            if state:
                set_peer_status(peer[0], 2)
                state = check_peer_status(
                    peer_object["device_ip"],
                    peer_object["remote_ip"],
                    device)
                if state:
                    print_error("Peer is established")
                    set_peer_status(peer[0], 3)
            else:
                fails.append(peer[0])

        elif peer[8] == 1:  # Started to configured but didn't work
            print_error("Peer id {} was attempted to be configured previously but there was a problem".format(peer[0]))
            # EMAIL NETWORK ENGINEER OR SOMETHING SIMILAR

        elif peer[8] == 2:  # Configured but wasn't in established state
            checks.append(peer[0])
            print_error("Checking peer: {}".format(peer))
            state = check_peer_workflow(peer)
            if state:
                print_error("Peer is established")
                set_peer_status(peer[0], 3)
            else:
                print_error("Peer still not established")
                fails.append(peer[0])

        else:  # Peer fully configured
            pass
    # Setting peer status to in progress
    return configures, checks, fails, non_approved
def check_peer_status(device_ip, remote_ip, device):
    """
    Author - Jonathan Steward
    Function - polls SNMP for the state of the Peer configured and returns details
    Inputs - 
        string - device_ip - ip address of the device with the target peer
        string - remote_ip - ip address of neighbour
        object - device - Device object storing all details 
    returns -
        bool - state of peer, if established true 
    """
    print"waiting for BGP to connect after configuration"
    time.sleep(30)

    result = SnmpPoll(
        "GET",
        ".1.3.6.1.2.1.15.3.1.2.{}".format(remote_ip),
        device_ip,
        device.community)
    print result.value
    if result.value == "1":
        print_error("neighbour is idle")
    elif result.value == "2":
        print_error("neighbour is in connect state")
    elif result.value == "3":
        print_error("neighbour is active")
    elif result.value == "4":
        print_error("neighbour is in open sent state")
    elif result.value == "5":
        print_error("neighbour is openconfirm")
    elif result.value == "6":
        print_error("neighbour is established")
    else:
        print_error("neighbour wasn't at all configured")

    if result.value == "6":
        return True
    return False
def configure_peer_cisco(peer, device):
    """
    Author - Jonathan Steward
    Function - Steps through the different configuration commands to configure a peer on cisco
    Inputs - 
        dict - peer - details of peer
        object - device - details of device
    returns -
        bool - state of configuration
    """
    connection = ssh_connection(device)
    if not connection:
        return False

    # Attempt to log into config
    command = ("\nen\n{}\nconfig t\n\n\n\n\n".format(device.enablePassword))
    connection.send(command)
    time.sleep(0.5)
    output = connection.recv(8000)
    if "#" not in output:
        print_error("password was wrong!, see below for the issue:\n{}".format(output))
        return False
    print output

    command = "\nrouter bgp {}\n".format(peer["local_asn"])
    connection.send(command)
    time.sleep(0.5)
    output = connection.recv(8000)
    if "(config-router)" not in output:
        print_error("Local BGP ASN defined in database is incorrect:\n{}".format(output))
        return False
    print output

    command = "\nneighbor {} remote-as {}\n".format(peer["remote_ip"], peer["remote_asn"])
    connection.send(command)
    time.sleep(0.5)
    output = connection.recv(8000)
    if "% Invalid" in output:
        print_error("Something went wrong with configuration:\n{}".format(output))
        return False
    print output

    if peer["loopback_source"]:
        command = "\nneighbor {} update-source {}\n".format(peer["remote_ip"], peer["source_interface"])
        connection.send(command)
        time.sleep(0.5)
        output = connection.recv(8000)
        if "% Invalid" in output:
            print_error("Something went wrong with configuration:\n{}".format(output))
            return False
        print output

        if peer["remote_asn"] != device.asn:  # Need to configure for ebgp multi hop
            command = "\nneighbor {} ebgp-multihop 5\n".format(peer["remote_ip"])
            connection.send(command)
            time.sleep(0.5)
            output = connection.recv(8000)
            if "% Invalid" in output:
                print_error("Something went wrong with configuration:\n{}".format(output))
                return False
            print output

    print_error("configured BGP neighbour")
    
    command = "\nend\ncopy run start\n"
    connection.send(command)
    time.sleep(1)
    
    return True