Beispiel #1
0
def deploy(hostname=None, acls=None, transport='ssh', save_config=False,
           timeout=60):
    """
    Deploy code in a safe way o a Cisco NX-OS device.
    """
    try:
        username, enable_pass, password = \
            netrc.netrc().authenticators(hostname)
        account = Account(name=username, password=password,
                          password2=enable_pass)
    except:
        print("ERROR: could not find device in ~/.netrc file")
        print("HINT: either update .netrc or enter username + pass now.")
        try:
            account = read_login()
        except EOFError:
            print("ERROR: could not find proper username + pass")
            print("HINT: set username & pass in ~/.netrc for device %s"
                  % hostname)
            import sys
            sys.exit(2)

    def s(conn, line):
        print("   %s" % line)
        conn.execute(line)

    def collect_interfaces(conn):
        template = """# textfsm
Value Required Interface ([^ ]+)
Value Inbound (.*)
Value Outbound (.*)

Start
  ^${Interface} is up
  ^  Outgoing access list is ${Outbound}
  ^  Inbound  access list is ${Inbound} -> Record Start

"""
        template_file = StringIO(template)
        table = textfsm.TextFSM(template_file)
        s(conn, 'show ip int | inc ine pro|list is')
        interface_acl_v4 = table.ParseText(conn.response)

        template = """# textfsm
Value Required Interface ([^ ]+)
Value Inbound (.*)
Value Outbound (.*)

Start
  ^${Interface} is up
  ^  Inbound access list ${Inbound}
  ^  Outgoing access list ${Outbound} -> Record Start

"""
        template_file = StringIO(template)
        table = textfsm.TextFSM(template_file)
        s(conn, 'show ipv6 int  | i ine pro|access list')
        interface_acl_v6 = table.ParseText(conn.response)
        template = """# textfsm
Value Required Vty (\d+\s\d+)
Value Inbound4 ([^ ]+)
Value Outbound4 ([^ ]+)
Value Inbound6 ([^ ]+)
Value Outbound6 ([^ ]+)

Start
  ^line vty ${Vty}
  ^ access-class ${Inbound4} in
  ^ access-class ${Outbound4} out
  ^ ipv6 access-class ${Inbound6} in
  ^ ipv6 access-class ${Outbound6} out -> Record Start

"""
        template_file = StringIO(template)
        table = textfsm.TextFSM(template_file)
        s(conn, 'show run | begin ^line vty')
        interface_acl_vty = table.ParseText(conn.response)

        results = {4: interface_acl_v4, 6: interface_acl_v6}
        # add vty lines
        for vty in interface_acl_vty:
            # v4 inbound
            v4_inbound = vty[1] if vty[1] else "not set"
            v4_outbound = vty[2] if vty[1] else "not set"
            v6_inbound = vty[3] if vty[1] else "not set"
            v6_outbound = vty[4] if vty[1] else "not set"
            results[4].append(["vty %s" % vty[0], v4_inbound, v4_outbound])
            results[6].append(["vty %s" % vty[0], v6_inbound, v6_outbound])
        return results

    # main flow of the program starts here
    if transport == 'ssh':
        conn = SSH2(verify_fingerprint=False, debug=0, timeout=timeout)
    elif transport == 'telnet':
        conn = Telnet(debug=0)
    else:
        print("ERROR: Unknown transport mechanism: %s"
              % transport)
        sys.exit(2)
    conn.set_driver('nxos')
    conn.connect(hostname)
    conn.login(account)
    conn.execute('terminal length 0')
    conn.auto_app_authorize(account)
    capabilities = {}
    s(conn, "show ipv6 cef")
    capabilities['ipv6'] = False if "%IPv6 CEF not running" in conn.response else True
    if capabilities['ipv6']:
        print("INFO: IPv6 support detected")
    else:
        print("INFO: NO IPv6 support detected, skipping IPv6 ACLs")
    # map into structure:
    # policyname { (int, afi, direction) }
    map_pol_int = {}
    interfaces_overview = collect_interfaces(conn)
    for afi in interfaces_overview:
        for interface, inbound, outbound in interfaces_overview[afi]:
            # add inbound rules to map
            if inbound not in map_pol_int.keys():
                map_pol_int[inbound] = [{"int": interface,
                                        "afi": afi,
                                        "dir": "in"}]
            else:
                map_pol_int[inbound].append({"int": interface,
                                             "afi": afi,
                                             "dir": "in"})
            # add outbound
            if outbound not in map_pol_int.keys():
                map_pol_int[outbound] = [{"int": interface,
                                          "afi": afi,
                                          "dir": "in"}]
            else:
                map_pol_int[outbound].append({"int": interface,
                                             "afi": afi,
                                             "dir": "out"})
    print("INFO: interface / policy mapping:")
    pprint(map_pol_int)

    def lock_step(lock, pol, capabilities):
        name = acls[pol]['name']
        afi = acls[pol]['afi']
        if afi == 6 and not capabilities['ipv6']:
            return
        policy = acls[pol]['policy']
        print("INFO: uploading name: %s, afi: %s" % (name, afi))
        s(conn, 'configure session aclhound')
        if afi == 4:
            try:
                s(conn, "no ip access-list %s%s" % (lock, name))
            except:
                pass
            s(conn, "ip access-list %s%s" % (lock, name))
            for line in policy.split('\n'):
                s(conn, line)
        if afi == 6:
            try:
                s(conn, "no ipv6 access-list %s%s" % (lock, name))
            except:
                pass
            s(conn, "ipv6 access-list %s%s" % (lock, name))
            for line in policy.split('\n'):
                s(conn, line)
        s(conn, "commit")


    for policy in acls:
            lock_step("", policy, capabilities)

    if save_config == True:
        s(conn, "copy running-config startup-config")
Beispiel #2
0
def deploy(hostname=None, acls=None, transport='ssh', save_config=False,
           timeout=60):
    """
    Deploy code in a safe way o a Cisco IOS device.
    """
    try:
        username, enable_pass, password = \
            netrc.netrc().authenticators(hostname)
        account = Account(name=username, password=password,
                          password2=enable_pass)
    except:
        print("ERROR: could not find device in ~/.netrc file")
        print("HINT: either update .netrc or enter username + pass now.")
        try:
            account = read_login()
        except EOFError:
            print("ERROR: could not find proper username + pass")
            print("HINT: set username & pass in ~/.netrc for device %s"
                  % hostname)
            import sys
            sys.exit(2)

    def s(conn, line):
        print("   %s" % line)
        conn.execute(line)

    def collect_interfaces(conn):
        template = """# textfsm
Value Required Interface ([^ ]+)
Value Inbound (.*)
Value Outbound (.*)

Start
  ^${Interface} is up
  ^  Outgoing access list is ${Outbound}
  ^  Inbound  access list is ${Inbound} -> Record Start

"""
        template_file = StringIO(template)
        table = textfsm.TextFSM(template_file)
        s(conn, 'show ip int | inc ine pro|list is')
        interface_acl_v4 = table.ParseText(conn.response)

        template = """# textfsm
Value Required Interface ([^ ]+)
Value Inbound (.*)
Value Outbound (.*)

Start
  ^${Interface} is up
  ^  Inbound access list ${Inbound}
  ^  Outgoing access list ${Outbound} -> Record Start

"""
        template_file = StringIO(template)
        table = textfsm.TextFSM(template_file)
        s(conn, 'show ipv6 int  | i ine pro|access list')
        interface_acl_v6 = table.ParseText(conn.response)
        template = """# textfsm
Value Required Vty (\d+\s\d+)
Value Inbound4 ([^ ]+)
Value Outbound4 ([^ ]+)
Value Inbound6 ([^ ]+)
Value Outbound6 ([^ ]+)

Start
  ^line vty ${Vty}
  ^ access-class ${Inbound4} in
  ^ access-class ${Outbound4} out
  ^ ipv6 access-class ${Inbound6} in
  ^ ipv6 access-class ${Outbound6} out -> Record Start

"""
        template_file = StringIO(template)
        table = textfsm.TextFSM(template_file)
        s(conn, 'show run | begin ^line vty')
        interface_acl_vty = table.ParseText(conn.response)

        results = {4: interface_acl_v4, 6: interface_acl_v6}
        # add vty lines
        for vty in interface_acl_vty:
            # v4 inbound
            v4_inbound = vty[1] if vty[1] else "not set"
            v4_outbound = vty[2] if vty[1] else "not set"
            v6_inbound = vty[3] if vty[1] else "not set"
            v6_outbound = vty[4] if vty[1] else "not set"
            results[4].append(["vty %s" % vty[0], v4_inbound, v4_outbound])
            results[6].append(["vty %s" % vty[0], v6_inbound, v6_outbound])
        return results

    # main flow of the program starts here
    if transport == 'ssh':
        conn = SSH2(verify_fingerprint=False, debug=0, timeout=timeout)
    elif transport == 'telnet':
        conn = Telnet(debug=0)
    else:
        print("ERROR: Unknown transport mechanism: %s"
              % transport)
        sys.exit(2)
    conn.set_driver('ios')
    conn.connect(hostname)
    conn.login(account)
    conn.execute('terminal length 0')
    conn.auto_app_authorize(account)
    capabilities = {}
    s(conn, "show ipv6 cef")
    capabilities['ipv6'] = False if "%IPv6 CEF not running" in conn.response else True
    if capabilities['ipv6']:
        print("INFO: IPv6 support detected")
    else:
        print("INFO: NO IPv6 support detected, skipping IPv6 ACLs")
    # map into structure:
    # policyname { (int, afi, direction) }
    map_pol_int = {}
    interfaces_overview = collect_interfaces(conn)
    for afi in interfaces_overview:
        for interface, inbound, outbound in interfaces_overview[afi]:
            # add inbound rules to map
            if inbound not in map_pol_int.keys():
                map_pol_int[inbound] = [{"int": interface,
                                        "afi": afi,
                                        "dir": "in"}]
            else:
                map_pol_int[inbound].append({"int": interface,
                                             "afi": afi,
                                             "dir": "in"})
            # add outbound
            if outbound not in map_pol_int.keys():
                map_pol_int[outbound] = [{"int": interface,
                                          "afi": afi,
                                          "dir": "in"}]
            else:
                map_pol_int[outbound].append({"int": interface,
                                             "afi": afi,
                                             "dir": "out"})
    print("INFO: interface / policy mapping:")
    pprint(map_pol_int)

    def lock_step(lock, pol, capabilities):
        name = acls[pol]['name']
        afi = acls[pol]['afi']
        if afi == 6 and not capabilities['ipv6']:
            return
        policy = acls[pol]['policy']
        print("INFO: uploading name: %s, afi: %s" % (name, afi))
        s(conn, 'configure terminal')
        if afi == 4:
            try:
                s(conn, "no ip access-list extended %s%s" % (lock, name))
            except:
                pass
            s(conn, "ip access-list extended %s%s" % (lock, name))
            for line in policy.split('\n'):
                s(conn, line)
        if afi == 6:
            try:
                s(conn, "no ipv6 access-list %s%s" % (lock, name))
            except:
                pass
            s(conn, "ipv6 access-list %s%s" % (lock, name))
            for line in policy.split('\n'):
                s(conn, line)
        s(conn, "end")

        # then replace ACL on all interfaces / VTYs
        if name in map_pol_int:
            for entry in map_pol_int[name]:
                if not entry['afi'] == afi:
                    continue
                print("INFO: lockstepping policy %s afi %s" % (name, afi))
                s(conn, "configure terminal")
                if entry['int'].startswith('vty '):
                    s(conn, "line %s" % entry['int'])
                    if afi == 4:
                        s(conn, "access-class %s%s %s"
                          % (lock, name, entry['dir']))
                    if afi == 6:
                        s(conn, "ipv6 access-class %s%s %s"
                          % (lock, name, entry['dir']))
                else:
                    s(conn, "interface %s" % entry['int'])
                    if afi == 4:
                        s(conn, "ip access-group %s%s %s"
                          % (lock, name, entry['dir']))
                    if afi == 6:
                        s(conn, "ipv6 traffic-filter %s%s %s"
                          % (lock, name, entry['dir']))
                s(conn, "end")

    for policy in acls:
        for lock in ["LOCKSTEP-", ""]:
            lock_step(lock, policy, capabilities)
        # cleanup
        s(conn, "configure terminal")
        if acls[policy]['afi'] == 4:
            s(conn, "no ip access-list extended LOCKSTEP-%s"
              % acls[policy]['name'])
        if acls[policy]['afi'] == 6 and capabilities['ipv6']:
            s(conn, "no ipv6 access-list LOCKSTEP-%s"
              % acls[policy]['name'])
        s(conn, "end")

    if save_config == True:
        s(conn, "write")