Skip to content

Commit

Permalink
Merge pull request #135 from netdevops/v3-docs
Browse files Browse the repository at this point in the history
V3 docs
  • Loading branch information
jtdub authored Nov 18, 2024
2 parents 006e8d8 + fe69854 commit 5baa554
Show file tree
Hide file tree
Showing 14 changed files with 1,453 additions and 47 deletions.
90 changes: 90 additions & 0 deletions docs/config-view.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Config View

A Config View is an abstraction layer for network device configurations. It provides a structured, Pythonic way to interact with and extract information from raw configuration data. Config Views are especially useful for standardizing how configuration elements are accessed across different platforms and devices.

The framework uses a combination of abstract base classes (e.g., `ConfigViewInterfaceBase`, `HConfigViewBase`) and platform-specific implementations (e.g., `ConfigViewInterfaceCiscoIOS`, `HConfigViewCiscoIOS`) to provide a unified interface for interacting with configurations while accounting for the unique syntax and semantics of each vendor or platform.

## Why Use Config Views?

1. **Vendor Abstraction:** Network devices from different vendors (Cisco, Arista, Juniper, etc.) have varied configuration formats. Config Views standardize access, making it easier to work across platforms.

2. **Simplified Interface:** Accessing configuration data becomes more intuitive through Python properties and methods rather than manually parsing text.

3. **Extensibility:** Easily extendable to support new platforms or devices by implementing platform-specific subclasses.

4. **Error Reduction:** Encapsulates parsing logic, reducing the risk of errors due to configuration syntax differences.

## Available Config Views

| **Property/Method** | **Type** | **Description** |
|-----------------------------|--------------------------------|------------------------------------------------------------------------------|
| `bundle_interface_views` | `Iterable` | Yields interfaces configured as bundles. |
| `config` | `HConfig` | Root configuration object. |
| `dot1q_mode_from_vlans` | `Callable` | Determines 802.1Q mode based on VLANs and tagging. |
| `hostname` | `Optional[str]` | Retrieves the device hostname. |
| `interface_names_mentioned` | `frozenset[str]` | Set of all interface names mentioned. |
| `interface_view_by_name` | `Callable` | Returns view of a specific interface by name. |
| `interface_views` | `Iterable` | Yields all interface views. |
| `interfaces` | `Iterable[HConfigChild]` | Yields raw configuration objects for all interfaces. |
| `interfaces_names` | `Iterable[str]` | Yields the names of all interfaces. |
| `ipv4_default_gw` | `Optional[IPv4Address]` | Retrieves the IPv4 default gateway. |
| `location` | `str` | Returns the SNMP location. |
| `module_numbers` | `Iterable[int]` | Yields module numbers from interfaces. |
| `stack_members` | `Iterable[StackMember]` | Yields stack members configured on the device. |
| `vlan_ids` | `frozenset[int]` | Set of VLAN IDs configured. |
| `vlans` | `Iterable[Vlan]` | Yields VLAN objects, including ID and name. |

## Example: Cisco IOS Config View

### Step 1: Parse Configuration

Assume we have a Cisco IOS configuration file as a string.

```python
from hier_config import Platform, get_hconfig


raw_config = """
hostname router1
interface GigabitEthernet0/1
description Uplink to Switch
switchport access vlan 10
ip address 192.168.1.1 255.255.255.0
shutdown
!
vlan 10
name DATA
"""

hconfig = get_hconfig(Platform.CISCO_IOS, raw_config)
```

### Step 2: Create Config View

```python
from hier_config.platforms.cisco_ios.view import HConfigViewCiscoIOS


config_view = HConfigViewCiscoIOS(hconfig)
```

### Step 3: Access Configuration Details

Access properties to interact with the configuration programmatically:

```python
# Get the hostname
print(config_view.hostname) # Output: router1

# List all interface names
print(list(config_view.interfaces_names)) # Output: ['GigabitEthernet0/1']

# Check if an interface is enabled
for interface_view in config_view.interface_views:
print(interface_view.name, "Enabled:", interface_view.enabled)

# Get all VLANs
for vlan in config_view.vlans:
print(f"VLAN {vlan.id}: {vlan.name}")

```
221 changes: 221 additions & 0 deletions docs/custom-workflows.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
# Creating Custom Workflows

Certain scenarios demand remediation strategies that go beyond the standard negation and idempotency workflows Hier Config is designed to handle. To address these edge cases, Hier Config allows for custom remediation workflows that integrate seamlessly with the existing remediation process.

----

## Building a Remediation Workflow

1. Importing Modules and Loading Configurations

Start by importing the necessary modules and loading the running and intended configurations for comparison.

```python
from hier_config import WorkflowRemediation, get_hconfig, Platform
from hier_config.utils import load_device_config
```

Load the configurations from files:

```python
running_config = load_device_config("./tests/fixtures/running_config_acl.conf")
generated_config = load_device_config("./tests/fixtures/generated_config_acl.conf")
```

These configurations represent the current and desired states of the device.

----

2. Initializing the Workflow Remediation Object:

Initialize the WorkflowRemediation object for a Cisco IOS platform:


```python
wfr = WorkflowRemediation(
running_config=get_hconfig(Platform.CISCO_IOS, running_config),
generated_config=get_hconfig(Platform.CISCO_IOS, generated_config)
)
```
This object manages the remediation workflow between the running and generated configurations.

----

## Extracting and Analyzing Remediation Sections

### Example: Access-List Custom Remediation

**Current (Running) Configuration**

```python
print(wfr.running_config.get_child(startswith="ip access-list"))
```

Output:

```
ip access-list extended TEST
12 permit ip 10.0.0.0 0.0.0.7 any
exit
```

**Intended (Generated) Configuration**

```python
print(wfr.generated_config.get_child(startswith="ip access-list"))
```

Output:

```
ip access-list extended TEST
10 permit ip 10.0.1.0 0.0.0.255 any
20 permit ip 10.0.0.0 0.0.0.7 any
exit
```

**Default Remediation Configuration**

```python
print(wfr.remediation_config.get_child(startswith="ip access-list"))
```

Output:

```
ip access-list extended TEST
no 12 permit ip 10.0.0.0 0.0.0.7 any
10 permit ip 10.0.1.0 0.0.0.255 any
20 permit ip 10.0.0.0 0.0.0.7 any
exit
```

#### Issues with the Default Remediation:

1. **Invalid Command:** `no 12 permit ip 10.0.0.0 0.0.0.7 any` is invalid in Cisco IOS. The valid command is `no 12`.
2. **Risk of Lockout:** Removing a line currently matched by traffic could cause a connectivity outage.
3. **Unnecessary Changes:** `permit ip 10.0.0.0 0.0.0.7 any` is a valid line aside from sequence numbers. In large ACLs, this might be unnecessary to delete and re-add.

----

#### Goals for Safe Access-List Remediation

To avoid outages during production changes:

1. **Resequence the ACL:** Adjust sequence numbers using the ip access-list resequence command.
* For demonstration, resequence to align 12 to 20.
2. **Temporary Allow-All:** Add a temporary rule (1 permit ip any any) to prevent lockouts.
2. **Cleanup:** Remove the temporary rule (no 1) after applying the changes.

----

## Building the Custom Remediation

1. Create a Custom `HConfig` Object

```python
from hier_config import HConfig

custom_remediation = HConfig(wfr.running_config.driver)
```

2. Add Resequencing and Extract ACL Remediation

```python
custom_remediation.add_child("ip access-list resequence TEST 10 10")
custom_remediation.add_child("ip access-list extended TEST")
remediation = wfr.remediation_config.get_child(equals="ip access-list extended TEST")
```

3. Build the Custom ACL Remediation

```python
acl = custom_remediation.get_child(equals="ip access-list extended TEST")
acl.add_child("1 permit ip any any") # Temporary allow-all

for line in remediation.all_children():
if line.text.startswith("no "):
# Adjust invalid sequence negation
parts = line.text.split()
rounded_number = round(int(parts[1]), -1)
acl.add_child(f"{parts[0]} {rounded_number}")
else:
acl.add_child(line.text)

acl.add_child("no 1") # Cleanup temporary rule
```

### Output of Custom Remediation

```python
print(custom_remediation)
```

Output:

```
ip access-list resequence TEST 10 10
ip access-list extended TEST
1 permit ip any any
no 10
10 permit ip 10.0.1.0 0.0.0.255 any
20 permit ip 10.0.0.0 0.0.0.7 any
no 1
exit
```

## Applying the Custom Remediation

### Remove Invalid Remediation

```python
invalid_remediation = wfr.remediation_config.get_child(equals="ip access-list extended TEST")
wfr.remediation_config.delete_child(invalid_remediation)
```

### Add Custom Remediation

```python
wfr.remediation_config.merge(custom_remediation)
```

### Output of Updated Remediation

```python
print(wfr.remediation_config)
```

Output:

```
vlan 3
name switch_mgmt_10.0.3.0/24
exit
vlan 4
name switch_mgmt_10.0.4.0/24
exit
interface Vlan2
mtu 9000
ip access-group TEST in
no shutdown
exit
interface Vlan3
description switch_mgmt_10.0.3.0/24
ip address 10.0.3.1 255.255.0.0
exit
interface Vlan4
mtu 9000
description switch_mgmt_10.0.4.0/24
ip address 10.0.4.1 255.255.0.0
ip access-group TEST in
no shutdown
exit
ip access-list resequence TEST 10 10
ip access-list extended TEST
1 permit ip any any
no 10
10 permit ip 10.0.1.0 0.0.0.255 any
20 permit ip 10.0.0.0 0.0.0.7 any
no 1
exit
```
Loading

0 comments on commit 5baa554

Please sign in to comment.