Skip to content

Commit

Permalink
add docs on advanced netmiko extras, add new test case for single com… (
Browse files Browse the repository at this point in the history
#284)

* add docs on advanced netmiko extras, add new test case for single command validations
  • Loading branch information
jeffkala authored Dec 13, 2024
1 parent 94db175 commit 4c66d03
Show file tree
Hide file tree
Showing 6 changed files with 191 additions and 0 deletions.
2 changes: 2 additions & 0 deletions changes/189.housekeeping
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Added documentation on using ssh public key authentication.
- Added documentation on using ssh proxy via jumphost.
96 changes: 96 additions & 0 deletions docs/user/app_use_cases.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,102 @@ PLUGINS_CONFIG = {

When the on-demand inventory is created for the `Sync Device from Network` job, the extras in the `netmiko` connection dictionary are added to the connection setup.

### Using SSH PubKey Authentication

In the case where you want to use SSH Public Key authentication that can be accomplished by adding the additional arguments into Netmiko. This is done using the plugin configuration.

1. Create the ssh key on the Nautobot worker server/container.

2. Add the Netmiko Extras to the configuration.

```python
PLUGINS_CONFIG = {
"nautobot_device_onboarding": {},
"nautobot_ssot": {
"hide_example_jobs": True,
},
"nautobot_plugin_nornir": {
"nornir_settings": {
"credentials": "nautobot_plugin_nornir.plugins.credentials.nautobot_secrets.CredentialsNautobotSecrets",
"runner": {
"plugin": "threaded",
"options": {
"num_workers": 20,
},
},
},
"connection_options": {
"netmiko": {
"extras": {
"use_keys": True,
"key_file": "/root/.ssh/id_rsa.pub",
"disabled_algorithms": {"pubkeys": ["rsa-sha2-256", "rsa-sha2-512"]},
},
},
},
},
}
```

3. Make a secrets group in Nautobot which still had all the elements (username and password), where the username is accurate, a bogus password can be used as its ignored by the backend processing. For example, set the password to the username secret since its ignore.

4. Run the jobs and ssh public key authentication will be used.

### Using SSH Proxy Jumphost

In the case where you want to use a SSH proxy jumphost, it can be accomplished by adding the additional arguments into Netmiko. This is done using the plugin configuration.

1. Follow the standard Jumphost proxy setup to create the ssh_config file with the proper settings.

For example:

```
root@fcdc254e2a36:/source# cat /root/.ssh/config
host jumphost
IdentitiesOnly yes
IdentityFile ~/.ssh/id_rsa
User ntc
HostName 10.1.1.10
host * !jumphost
User admin
KexAlgorithms +diffie-hellman-group1-sha1,diffie-hellman-group14-sha1,diffie-hellman-group-exchange-sha1
HostKeyAlgorithms +ssh-rsa
ProxyCommand ssh -F /root/.ssh/config -W %h:%p jumphost
```

2. Add the Netmiko Extras to the configuration.

```python
PLUGINS_CONFIG = {
"nautobot_device_onboarding": {},
"nautobot_ssot": {
"hide_example_jobs": True,
},
"nautobot_plugin_nornir": {
"nornir_settings": {
"credentials": "nautobot_plugin_nornir.plugins.credentials.nautobot_secrets.CredentialsNautobotSecrets",
"runner": {
"plugin": "threaded",
"options": {
"num_workers": 20,
},
},
},
"connection_options": {
"netmiko": {
"extras": {
"ssh_config_file": "/root/.ssh/config",
},
},
},
},
}
```

3. Run the jobs and the ssh config will be used and the connection will be proxied through the jumphost.

# Use-cases and common workflows

## Onboarding a Device
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[
{
"software_image": "C3560CX-UNIVERSALK9-M",
"version": "15.2(7)E9",
"release": "fc1",
"rommon": "Bootstrap",
"hostname": "IOS-SW-1",
"uptime": "18 hours, 30 minutes",
"uptime_years": "",
"uptime_weeks": "",
"uptime_days": "",
"uptime_hours": "18",
"uptime_minutes": "30",
"reload_reason": "power-on",
"running_image": "c3560cx-universalk9-mz.152-7.E9.bin",
"hardware": [
"WS-C3560CX-12PC-S"
],
"serial": [
"FOC2341Y2CQ"
],
"config_register": "0xF",
"mac_address": [
"6C:71:0D:1D:35:80"
],
"restarted": "20:40:50 UTC Thu May 30 2024"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"IOS-SW-1"
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
command: "show version"
parser: "textfsm"
jpath: "[*].hostname"
60 changes: 60 additions & 0 deletions nautobot_device_onboarding/tests/test_extract_and_process.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"""Test for single command, extraction, and processing."""

import json
import os
import unittest

import yaml
from nornir.core.inventory import ConnectionOptions, Defaults, Host

from nautobot_device_onboarding.nornir_plays.formatter import extract_and_post_process

MOCK_DIR = os.path.join("nautobot_device_onboarding", "tests", "mock")


class TestSingleCommandFormatterExtractAndProcess(unittest.TestCase):
"""Test for single command, extraction, and processing."""

def setUp(self):
self.host = Host(
name="198.51.100.1",
hostname="198.51.100.1",
port=22,
username="username",
password="password", # nosec
platform="not_used_here",
connection_options={
"netmiko": ConnectionOptions(
hostname="198.51.100.1",
port=22,
username="username",
password="password", # nosec
platform="platform",
)
},
defaults=Defaults(data={"sync_vlans": False, "sync_vrfs": False, "sync_cables": False}),
)

def test_extract_and_process_from_directory(self):
test_dir = f"{MOCK_DIR}/extract_and_process/"
for subdir in os.listdir(test_dir):
subdir_path = os.path.join(test_dir, subdir)
if os.path.isdir(subdir_path):
with self.subTest(subdir=subdir):
with open(
os.path.join(subdir_path, "partial_command_mapper.yml"), "r", encoding="utf-8"
) as parsing_info:
platform_parsing_info = yaml.safe_load(parsing_info)
with open(os.path.join(subdir_path, "command_output"), "r", encoding="utf-8") as command_info:
command_outputs = json.loads(command_info.read())
with open(os.path.join(subdir_path, "expected_result"), "r", encoding="utf-8") as expected_info:
expected_result = json.loads(expected_info.read())

_, postpro_result = extract_and_post_process(
command_outputs,
platform_parsing_info,
{"obj": self.host.name, "original_host": self.host.name},
None,
False,
)
self.assertEqual(expected_result, postpro_result)

0 comments on commit 4c66d03

Please sign in to comment.