Prefix-List Operations (Coming Soon!)
==============
Description
--------------

Prefix-List Ops has the following features:

+ View prefix-list
+ Find prefix by prefix or prefix length
+ Find overlapping prefixes in the same list (ge, le)
+ Find duplicate prefixes in different list

Depedency Modules
__________

+ ipaddress
+ re
+ collections
+ xmltodict
+ ncclient
+ socket

Device Modules Tested
---------------

**Different device modules return different configuration/XML. I've tested on the following**

+ ISR 4331
+ ASR 1001-X
+ Catalyst 3850

Usage
--------------

**Import**
            >>> import IPOperations.isr_lists as ipops

**Create Objects/Call Class**

            >>> call_class = ipops.IpOps(host="10.48.1.130", username="cisco", password="cisco")
            print(call_class)
            <IPOperations.isr_lists.IpOps object at 0x0000024A786C8248>

**View Prefix Lists**

            >>> call_class.view_prefix_list()
            local-prefixes
            5 permit 1.1.1.0/24 25
            10 permit 3.1.1.0/26 27
            15 permit 2.1.1.0/26 27

**Find Prefix Length**

            >>> call_class.find_prefix_length(length="25")
            defaultdict(<class 'list'>, {'test22': [{'seq': '25', 'prefix': '1.1.1.0/25'},
            {'seq': '30', 'prefix': '2.2.2.0/25'}],
            'test23': [{'seq': '25', 'prefix': '1.1.1.0/25'},
            {'seq': '30', 'prefix': '2.2.2.0/25'}]})

**Find Perfix**

            >>> call_class.find_prefix(prefix="1.1.1.0/24")
            defaultdict(<class 'list'>, {'local-prefixes': [{'seq': '5', 'prefix': '1.1.1.0/24'}],
            'test22': [{'seq': '5', 'prefix': '1.1.1.0/24'}],
            'test23': [{'seq': '5', 'prefix': '1.1.1.0/24'}]})

**Find Overlapping Prefixes**

            call_class.find_overlapping_prefixes()
            defaultdict(<class 'list'>,
            {'local-prefixes': [OrderedDict([('prefix', '1.1.1.0/24'), ('overlapping-seq', '5'), ('ge', '25'), ('le', '32'), ('overlapping-prefixes', ['1.1.1.0/25', '1.1.1.0/26', '1.1.1.0/27', '1.1.1.0/28', '1.1.1.0/29', '1.1.1.0/30', '1.1.1.0/31', '1.1.1.0/32'])]),
            OrderedDict([('prefix', '1.1.1.0/25'), ('overlapping-seq', '5'), ('ge', '25'), ('le', '32'), ('overlapping-prefixes', ['1.1.1.0/25', '1.1.1.0/26', '1.1.1.0/27', '1.1.1.0/28', '1.1.1.0/29', '1.1.1.0/30', '1.1.1.0/31', '1.1.1.0/32'])]),
            OrderedDict([('prefix', '1.0.0.0/8'), ('overlapping-seq', '5'), ('ge', '25'), ('le', '32'), ('overlapping-prefixes', ['1.1.1.0/25', '1.1.1.0/26', '1.1.1.0/27', '1.1.1.0/28', '1.1.1.0/29', '1.1.1.0/30', '1.1.1.0/31', '1.1.1.0/32'])]),
            OrderedDict([('prefix', '3.1.1.0/26'), ('overlapping-seq', '10'), ('ge', '27'), ('le', '32'), ('overlapping-prefixes', ['3.1.1.0/27', '3.1.1.0/28', '3.1.1.0/29', '3.1.1.0/30', '3.1.1.0/31', '3.1.1.0/32'])]),
            OrderedDict([('prefix', '2.1.1.0/26'), ('overlapping-seq', '15'), ('ge', '27'), ('le', '32'), ('overlapping-prefixes', ['2.1.1.0/27', '2.1.1.0/28', '2.1.1.0/29', '2.1.1.0/30', '2.1.1.0/31', '2.1.1.0/32'])]),
            OrderedDict([('prefix', '4.1.1.0/26'), ('overlapping-seq', '20'), ('ge', '27'), ('le', '32'), ('overlapping-prefixes', ['4.1.1.0/27', '4.1.1.0/28', '4.1.1.0/29', '4.1.1.0/30', '4.1.1.0/31', '4.1.1.0/32'])]),
            OrderedDict([('prefix', '5.1.1.0/26'), ('overlapping-seq', '25'), ('ge', '27'), ('le', '32'), ('overlapping-prefixes', ['5.1.1.0/27', '5.1.1.0/28', '5.1.1.0/29', '5.1.1.0/30', '5.1.1.0/31', '5.1.1.0/32'])]),
            OrderedDict([('prefix', '6.1.1.0/26'), ('overlapping-seq', '30'), ('ge', '27'), ('le', '32'), ('overlapping-prefixes', ['6.1.1.0/27', '6.1.1.0/28', '6.1.1.0/29', '6.1.1.0/30', '6.1.1.0/31', '6.1.1.0/32'])]),
            OrderedDict([('prefix', '7.1.1.0/26'), ('overlapping-seq', '35'), ('le', '27'), ('overlapping-prefixes', ['7.1.1.0/27', '7.1.1.0/25', '7.1.1.0/24'])])],

        **View Overlapping Prefixes, Formatted**

        >>> call_class.view_overlapping_prefixes()
        local-prefixes
        Prefix: 1.1.1.0/24
        Overlapping Sequence: 5
        Range: GE: 25
        Range: LE 32
        Overlapping Prefixes: 1.1.1.0/25, 1.1.1.0/26, 1.1.1.0/27, 1.1.1.0/28, 1.1.1.0/29, 1.1.1.0/30, 1.1.1.0/31, 1.1.1.0/32
        Prefix: 1.1.1.0/25
        Overlapping Sequence: 5
        Range: GE: 25
        Range: LE 32
        Overlapping Prefixes: 1.1.1.0/25, 1.1.1.0/26, 1.1.1.0/27, 1.1.1.0/28, 1.1.1.0/29, 1.1.1.0/30, 1.1.1.0/31, 1.1.1.0/32
        Prefix: 1.0.0.0/8
        Overlapping Sequence: 5
        Range: GE: 25
        Range: LE 32
        Overlapping Prefixes: 1.1.1.0/25, 1.1.1.0/26, 1.1.1.0/27, 1.1.1.0/28, 1.1.1.0/29, 1.1.1.0/30, 1.1.1.0/31, 1.1.1.0/32

**View Duplicate Prefixes**

        >>> call_class.duplicate_prefix()
        defaultdict(<class 'list'>, {'prefixes': [defaultdict(<class 'list'>,
        {'local-prefixes': [{'seq': '5', 'prefix': '1.1.1.0/24'}],
        'test22': [{'seq': '5', 'prefix': '1.1.1.0/24'}],
        'test23': [{'seq': '5', 'prefix': '1.1.1.0/24'}]})

**View Routing Prefixes (No next hop)**
**Example only shows local and connected. The method will display all routing protocols and types if configured**

        >>> call_class.get_routing_table()
        >>> call_class.routing_prefixes
        {'1.0.0.0/8': 'C', '1.1.1.1/32': 'L', '4.0.0.0/8': 'L', '4.4.4.0/24': 'C', '4.4.4.4/32': 'L', '5.0.0.0/8': 'L',
            '5.5.5.0/24': 'C', '5.5.5.5/32': 'L', '6.0.0.0/8': 'L', '6.6.6.0/24': 'C', '6.6.6.6/32': 'L', '7.0.0.0/8': 'L',
        '7.7.7.0/24': 'C', '7.7.7.7/32': 'L', '8.0.0.0/8': 'L', '8.8.8.0/24': 'C', '8.8.8.8/32': 'L', '9.0.0.0/8': 'L',
        '9.9.9.0/24': 'C', '9.9.9.9/32': 'L', '10.0.0.0/8': 'L', '10.1.3.0/24': 'S', '10.10.10.0/24': 'C', '10.10.10.10/32': 'L',
        '10.10.11.0/24': 'C', '10.10.11.1/32': 'L', '192.168.1.0/24': 'C'}

**Sending Prefixes**

    **Send with prefix only**

            >>> call_class.send_prefix_list(name="HelpMe", prefix="192.168.1.0/24", seq="5", action="permit")
            HelpMe
                5 permit 192.168.1.0/24

    **Send with prefix and ge**

            >>> call_class.send_prefix_list(name="HelpMe", prefix="192.168.2.0/24", seq="10", action="permit", ge="26")
            HelpMe
                5 permit 192.168.1.0/24
                10 permit 192.168.2.0/24 26

    **Send with prefix, ge, le**

            >>> call_class.send_prefix_list(name="HelpMe", prefix="192.168.3.0/24", seq="15", action="permit", ge="26", le="32")
            HelpMe
                5 permit 192.168.1.0/24
                10 permit 192.168.2.0/24 26
                15 permit 192.168.3.0/24 26 32

**When sending new prefixes, the following checks will be done**

    **Seqeunce Check**

        >>> call_class.send_prefix_list(name="HelpMe", prefix="172.16.1.0/24", seq="5", action="permit")
        Traceback (most recent call last):
          File "<input>", line 1, in <module>
          File "C:\Users\JoeSmo\PycharmProjects\IPOperations\IPOperations\isr_lists.py", line 422, in send_prefix_list
            raise ValueError("Sequence Exist")
        ValueError: Sequence Exist

    **Prefix Check**

        >>> call_class.send_prefix_list(name="HelpMe", prefix="192.168.1.0/24", seq="20", action="permit")
            Traceback (most recent call last):
              File "<input>", line 1, in <module>
              File "C:\Users\JoeSmo\PycharmProjects\IPOperations\IPOperations\isr_lists.py", line 424, in send_prefix_list
                raise ValueError("Prefix Exist")
            ValueError: Prefix Exist

    **Overlapping Prefix Check**

        >>> call_class.send_prefix_list(name="HelpMe", prefix="192.168.1.0/26", seq="20", action="permit")
            Traceback (most recent call last):
              File "<input>", line 1, in <module>
              File "C:\Users\JoeSmo\PycharmProjects\IPOperations\IPOperations\isr_lists.py", line 502, in send_prefix_list
                self._find_dups_internal(prefix=kwargs["prefix"])
              File "C:\JoeSmo\PycharmProjects\IPOperations\IPOperations\isr_lists.py", line 561, in _find_dups_internal
                raise ValueError("{} overlapps with {}".format(kwargs["prefix"], network))
            ValueError: 192.168.1.0/26 overlaps with 192.168.1.0/24

    **Routing Table Check**
    **Using netmiko to get the routing table, the prefix is compared to the destination prefixes. If the prefix is**
    **external the program will warn you of this. If your selection no, the prefix will be cancled and an expection will**
    **be thrown. If yes, the prefix will be added to the list.**

        >>> call_class.send_prefix_list(name="HelpMe", prefix="10.10.12.0/24", seq="30", action="permit")
        Prefix is external/not local, Are you sure you want to add (yes/no)?
        no
        Traceback (most recent call last):
          File "<input>", line 1, in <module>
          File "C:\Users\JoeSmo\PycharmProjects\IPOperations\IPOperations\isr_lists.py", line 442, in send_prefix_list
            raise ValueError("Prefix configuration aborted")
        ValueError: Prefix configuration aborted

