diff --git a/changes/313.fixed b/changes/313.fixed new file mode 100644 index 00000000..46303025 --- /dev/null +++ b/changes/313.fixed @@ -0,0 +1 @@ +Fixed issue running a sync job via CSV would raise an exception. diff --git a/nautobot_device_onboarding/jobs.py b/nautobot_device_onboarding/jobs.py index 25b0ebd1..60ca84bd 100755 --- a/nautobot_device_onboarding/jobs.py +++ b/nautobot_device_onboarding/jobs.py @@ -488,6 +488,11 @@ def run( self.memory_profiling = memory_profiling self.debug = debug + self.job_result.task_kwargs = { + "debug": debug, + "connectivity_test": kwargs["connectivity_test"], + } + if csv_file: self.processed_csv_data = self._process_csv_data(csv_file=csv_file) if self.processed_csv_data: @@ -496,10 +501,11 @@ def run( for ip_address in self.processed_csv_data: self.ip_addresses.append(ip_address) # prepare the task_kwargs needed by the CommandGetterDO job - self.job_result.task_kwargs = { - "debug": debug, - "csv_file": self.task_kwargs_csv_data, - } + self.job_result.task_kwargs.update( + { + "csv_file": self.task_kwargs_csv_data, + } + ) else: raise ValidationError(message="CSV check failed. No devices will be synced.") @@ -541,24 +547,24 @@ def run( self.secrets_group = secrets_group self.platform = platform - self.job_result.task_kwargs = { - "debug": debug, - "location": location, - "namespace": namespace, - "ip_addresses": ip_addresses, - "set_mgmt_only": set_mgmt_only, - "update_devices_without_primary_ip": update_devices_without_primary_ip, - "device_role": device_role, - "device_status": device_status, - "interface_status": interface_status, - "ip_address_status": ip_address_status, - "port": port, - "timeout": timeout, - "secrets_group": secrets_group, - "platform": platform, - "csv_file": "", - "connectivity_test": kwargs["connectivity_test"], - } + self.job_result.task_kwargs.update( + { + "location": location, + "namespace": namespace, + "ip_addresses": ip_addresses, + "set_mgmt_only": set_mgmt_only, + "update_devices_without_primary_ip": update_devices_without_primary_ip, + "device_role": device_role, + "device_status": device_status, + "interface_status": interface_status, + "ip_address_status": ip_address_status, + "port": port, + "timeout": timeout, + "secrets_group": secrets_group, + "platform": platform, + "csv_file": "", + } + ) super().run(dryrun, memory_profiling, *args, **kwargs) diff --git a/nautobot_device_onboarding/nornir_plays/command_getter.py b/nautobot_device_onboarding/nornir_plays/command_getter.py index 07255690..fc2e0fb4 100755 --- a/nautobot_device_onboarding/nornir_plays/command_getter.py +++ b/nautobot_device_onboarding/nornir_plays/command_getter.py @@ -118,7 +118,7 @@ def netmiko_send_commands( return Result( host=task.host, result=f"{task.host.name} has missing definitions in command_mapper YAML file.", failed=True ) - if orig_job_kwargs["connectivity_test"]: + if orig_job_kwargs.get("connectivity_test", False): if not tcp_ping(task.host.hostname, task.host.port): return Result( host=task.host, result=f"{task.host.name} failed connectivity check via tcp_ping.", failed=True diff --git a/nautobot_device_onboarding/tests/fixtures/all_required_fields.csv b/nautobot_device_onboarding/tests/fixtures/all_required_fields.csv new file mode 100644 index 00000000..9c4e7d46 --- /dev/null +++ b/nautobot_device_onboarding/tests/fixtures/all_required_fields.csv @@ -0,0 +1,2 @@ +ip_address_host,location_name,device_role_name,namespace,device_status_name,interface_status_name,ip_address_status_name,secrets_group_name,set_mgmt_only,update_devices_without_primary_ip,port,timeout +172.23.0.8,Site A Parent,Network,Global,Active,Active,Active,test secrets group,True,False,22,30 diff --git a/nautobot_device_onboarding/tests/test_jobs.py b/nautobot_device_onboarding/tests/test_jobs.py index bb81d950..023fbc1e 100644 --- a/nautobot_device_onboarding/tests/test_jobs.py +++ b/nautobot_device_onboarding/tests/test_jobs.py @@ -1,11 +1,13 @@ """Test Jobs.""" -from unittest.mock import patch +from unittest.mock import ANY, patch +from django.core.files.base import ContentFile from nautobot.apps.testing import create_job_result_and_run_job from nautobot.core.testing import TransactionTestCase from nautobot.dcim.models import Device, Interface, Manufacturer, Platform from nautobot.extras.choices import JobResultStatusChoices +from nautobot.extras.models import FileProxy from nautobot_device_onboarding import jobs from nautobot_device_onboarding.tests import utils @@ -120,6 +122,52 @@ def test_process_csv_data__empty_file(self): processed_csv_data = onboarding_job._process_csv_data(csv_file=csv_file) # pylint: disable=protected-access self.assertEqual(processed_csv_data, None) + @patch("nautobot_device_onboarding.diffsync.adapters.sync_devices_adapters.sync_devices_command_getter") + @patch.dict("os.environ", {"DEVICE_USER": "test_user", "DEVICE_PASS": "test_password"}) + def test_csv_process_pass_connectivity_test_flag(self, mock_sync_devices_command_getter): + """Ensure the 'connectivity_test' option is passed to the command_getter when a CSV is in-play.""" + with open("nautobot_device_onboarding/tests/fixtures/all_required_fields.csv", "rb") as csv_file: + csv_contents = csv_file.read() + + job_form_inputs = { + "debug": True, + "connectivity_test": "AnyWackyValueHere", + "dryrun": False, + "csv_file": FileProxy.objects.create( + name="onboarding.csv", file=ContentFile(csv_contents, name="onboarding.csv") + ).id, + "location": None, + "namespace": None, + "ip_addresses": None, + "port": None, + "timeout": None, + "set_mgmt_only": None, + "update_devices_without_primary_ip": None, + "device_role": None, + "device_status": None, + "interface_status": None, + "ip_address_status": None, + "secrets_group": None, + "platform": None, + "memory_profiling": False, + } + + create_job_result_and_run_job( + module="nautobot_device_onboarding.jobs", name="SSOTSyncDevices", **job_form_inputs + ) + self.assertEqual( + mock_sync_devices_command_getter.mock_calls[0].args, + ( + ANY, + 10, + { + "debug": True, + "connectivity_test": "AnyWackyValueHere", + "csv_file": {"172.23.0.8": {"port": 22, "timeout": 30, "secrets_group": ANY, "platform": ""}}, + }, + ), + ) + class SSOTSyncNetworkDataTestCase(TransactionTestCase): """Test SSOTSyncNetworkData class."""