Skip to content

Refactor of DNS resolving #112

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions netbox_onboarding/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""OnboardingTask Django model.

(c) 2020 Network To Code
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""

import socket

import netaddr
from netaddr.core import AddrFormatError

from .exceptions import OnboardException


def onboarding_task_fqdn_to_ip(ot):
"""Method to assure OT has FQDN resolved to IP address and rewritten into OT.

If it is a DNS name, attempt to resolve the DNS address and assign the IP address to the
name.

Returns:
None

Raises:
OnboardException("fail-general"):
When a prefix was entered for an IP address
OnboardException("fail-dns"):
When a Name lookup via DNS fails to resolve an IP address
"""
try:
# If successful, this is an IP address and can pass
netaddr.IPAddress(ot.ip_address)
# Raise an Exception for Prefix values
except ValueError:
raise OnboardException(reason="fail-general", message=f"ERROR appears a prefix was entered: {ot.ip_address}")
# An AddrFormatError exception means that there is not an IP address in the field, and should continue on
except AddrFormatError:
try:
# Perform DNS Lookup
ot.ip_address = socket.gethostbyname(ot.ip_address)
ot.save()
except socket.gaierror:
# DNS Lookup has failed, Raise an exception for unable to complete DNS lookup
raise OnboardException(reason="fail-dns", message=f"ERROR failed to complete DNS lookup: {ot.ip_address}")
45 changes: 0 additions & 45 deletions netbox_onboarding/netdev_keeper.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,9 @@
import logging
import socket

import netaddr
from django.conf import settings
from napalm import get_network_driver
from napalm.base.exceptions import ConnectionException, CommandErrorException
from netaddr.core import AddrFormatError
from netmiko.ssh_autodetect import SSHDetect
from netmiko.ssh_exception import NetMikoAuthenticationException
from netmiko.ssh_exception import NetMikoTimeoutException
Expand Down Expand Up @@ -96,46 +94,6 @@ def __init__( # pylint: disable=R0913
self.onboarding_class = StandaloneOnboarding
self.driver_addon_result = None

def check_ip(self):
"""Method to check if the IP address form field was an IP address.

If it is a DNS name, attempt to resolve the DNS address and assign the IP address to the
name.

Returns:
(bool): True if the IP address is an IP address, or a DNS entry was found and
reassignment of the ot.ip_address was done.
False if unable to find a device IP (error)

Raises:
OnboardException("fail-general"):
When a prefix was entered for an IP address
OnboardException("fail-dns"):
When a Name lookup via DNS fails to resolve an IP address
"""
try:
# Assign checked_ip to None for error handling
# If successful, this is an IP address and can pass
checked_ip = netaddr.IPAddress(self.hostname)
return True
# Catch when someone has put in a prefix address, raise an exception
except ValueError:
raise OnboardException(
reason="fail-general", message=f"ERROR appears a prefix was entered: {self.hostname}"
)
# An AddrFormatError exception means that there is not an IP address in the field, and should continue on
except AddrFormatError:
try:
# Do a lookup of name to get the IP address to connect to
checked_ip = socket.gethostbyname(self.hostname)
self.hostname = checked_ip
return True
except socket.gaierror:
# DNS Lookup has failed, Raise an exception for unable to complete DNS lookup
raise OnboardException(
reason="fail-dns", message=f"ERROR failed to complete DNS lookup: {self.hostname}"
)

def check_reachability(self):
"""Ensure that the device at the mgmt-ipaddr provided is reachable.

Expand Down Expand Up @@ -231,9 +189,6 @@ def get_onboarding_facts(self):
OnboardException('fail-general'):
Any other unexpected device comms failure.
"""
# Check to see if the IP address entered was an IP address or a DNS entry, get the IP address
self.check_ip()

self.check_reachability()

logger.info("COLLECT: device information %s", self.hostname)
Expand Down
21 changes: 8 additions & 13 deletions netbox_onboarding/tests/test_netdev_keeper.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
from dcim.models import Site, DeviceRole, Platform

from netbox_onboarding.exceptions import OnboardException
from netbox_onboarding.helpers import onboarding_task_fqdn_to_ip
from netbox_onboarding.models import OnboardingTask
from netbox_onboarding.netdev_keeper import NetdevKeeper


class NetdevKeeperTestCase(TestCase):
Expand All @@ -46,37 +46,32 @@ def setUp(self):
ip_address="192.0.2.1/32", site=self.site1, role=self.device_role1, platform=self.platform1
)

@mock.patch("netbox_onboarding.netdev_keeper.socket.gethostbyname")
@mock.patch("netbox_onboarding.helpers.socket.gethostbyname")
def test_check_ip(self, mock_get_hostbyname):
"""Check DNS to IP address."""
# Look up response value
mock_get_hostbyname.return_value = "192.0.2.1"

# Create a Device Keeper object of the device
ndk4 = NetdevKeeper(hostname=self.onboarding_task4.ip_address)

# Check that the IP address is returned
self.assertTrue(ndk4.check_ip())
# FQDN -> IP
onboarding_task_fqdn_to_ip(ot=self.onboarding_task4)

# Run the check to change the IP address
self.assertEqual(ndk4.hostname, "192.0.2.1")
self.assertEqual(self.onboarding_task4.ip_address, "192.0.2.1")

@mock.patch("netbox_onboarding.netdev_keeper.socket.gethostbyname")
@mock.patch("netbox_onboarding.helpers.socket.gethostbyname")
def test_failed_check_ip(self, mock_get_hostbyname):
"""Check DNS to IP address failing."""
# Look up a failed response
mock_get_hostbyname.side_effect = gaierror(8)
ndk5 = NetdevKeeper(hostname=self.onboarding_task5.ip_address)
ndk7 = NetdevKeeper(hostname=self.onboarding_task7.ip_address)

# Check for bad.local raising an exception
with self.assertRaises(OnboardException) as exc_info:
ndk5.check_ip()
onboarding_task_fqdn_to_ip(ot=self.onboarding_task5)
self.assertEqual(exc_info.exception.message, "ERROR failed to complete DNS lookup: bad.local")
self.assertEqual(exc_info.exception.reason, "fail-dns")

# Check for exception with prefix address entered
with self.assertRaises(OnboardException) as exc_info:
ndk7.check_ip()
onboarding_task_fqdn_to_ip(ot=self.onboarding_task7)
self.assertEqual(exc_info.exception.reason, "fail-prefix")
self.assertEqual(exc_info.exception.message, "ERROR appears a prefix was entered: 192.0.2.1/32")
4 changes: 4 additions & 0 deletions netbox_onboarding/worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from .choices import OnboardingFailChoices
from .choices import OnboardingStatusChoices
from .exceptions import OnboardException
from .helpers import onboarding_task_fqdn_to_ip
from .metrics import onboardingtask_results_counter
from .models import OnboardingDevice
from .models import OnboardingTask
Expand All @@ -43,6 +44,9 @@ def onboard_device(task_id, credentials): # pylint: disable=too-many-statements

ot = OnboardingTask.objects.get(id=task_id)

# Rewrite FQDN to IP for Onboarding Task
onboarding_task_fqdn_to_ip(ot)

logger.info("START: onboard device")
onboarded_device = None

Expand Down