From 37bf820c16eb649e62d0aaf800646adc2b81e436 Mon Sep 17 00:00:00 2001 From: Gary Snider <75227981+gsnider2195@users.noreply.github.com> Date: Thu, 24 Oct 2024 14:29:29 -0700 Subject: [PATCH] changelog, pylint --- changes/824.added | 1 + changes/824.housekeeping | 1 + nautobot_golden_config/api/serializers.py | 2 +- nautobot_golden_config/api/views.py | 10 ++++----- nautobot_golden_config/models.py | 1 + nautobot_golden_config/tests/test_api.py | 26 +++++++++++------------ 6 files changed, 22 insertions(+), 19 deletions(-) create mode 100644 changes/824.added create mode 100644 changes/824.housekeeping diff --git a/changes/824.added b/changes/824.added new file mode 100644 index 00000000..7a025d98 --- /dev/null +++ b/changes/824.added @@ -0,0 +1 @@ +Added a REST API endpoint for Jinja template developers to render intended configurations from templates in an arbitrary git repository. diff --git a/changes/824.housekeeping b/changes/824.housekeeping new file mode 100644 index 00000000..bb14b698 --- /dev/null +++ b/changes/824.housekeeping @@ -0,0 +1 @@ +Updated multiple tests to use the faster `setUpTestData` instead of `setUp`. Fixed incorrect base class on `ConfigPlanTest`. diff --git a/nautobot_golden_config/api/serializers.py b/nautobot_golden_config/api/serializers.py index 5fdda451..64ba2120 100644 --- a/nautobot_golden_config/api/serializers.py +++ b/nautobot_golden_config/api/serializers.py @@ -126,7 +126,7 @@ class Meta: read_only_fields = ["device", "plan_type", "feature", "config_set"] -class GenerateIntendedConfigSerializer(serializers.Serializer): +class GenerateIntendedConfigSerializer(serializers.Serializer): # pylint: disable=abstract-method """Serializer for GenerateIntendedConfigView.""" intended_config = serializers.CharField() diff --git a/nautobot_golden_config/api/views.py b/nautobot_golden_config/api/views.py index 5d66c4fc..568109d1 100644 --- a/nautobot_golden_config/api/views.py +++ b/nautobot_golden_config/api/views.py @@ -194,15 +194,15 @@ def _get_object(self, request, model, query_param): raise GenerateIntendedConfigException(f"Parameter {query_param} is required.") try: return model.objects.restrict(request.user, "view").get(pk=pk) - except model.DoesNotExist: - raise GenerateIntendedConfigException(f"{model.__name__} with id '{pk}' not found.") + except model.DoesNotExist as exc: + raise GenerateIntendedConfigException(f"{model.__name__} with id '{pk}' not found.") from exc def _get_jinja_template_path(self, settings, device, git_repository): """Get the Jinja template path for the device in the provided git repository.""" try: rendered_path = render_jinja2(template_code=settings.jinja_path_template, context={"obj": device}) except (TemplateSyntaxError, TemplateError) as exc: - raise GenerateIntendedConfigException(f"Error rendering Jinja path template: {exc}") + raise GenerateIntendedConfigException("Error rendering Jinja path template") from exc filesystem_path = Path(git_repository.filesystem_path) / rendered_path if not filesystem_path.is_file(): msg = f"Jinja template {filesystem_path} not found in git repository {git_repository}." @@ -238,7 +238,7 @@ def get(self, request, *args, **kwargs): try: ensure_git_repository(git_repository) except Exception as exc: - raise GenerateIntendedConfigException(f"Error trying to sync git repository: {exc}") + raise GenerateIntendedConfigException("Error trying to sync git repository") from exc filesystem_path = self._get_jinja_template_path(settings, device, git_repository) @@ -248,7 +248,7 @@ def get(self, request, *args, **kwargs): try: intended_config = render_jinja2(template_code=template_contents, context=context) except (TemplateSyntaxError, TemplateError) as exc: - raise GenerateIntendedConfigException(f"Error rendering Jinja template: {exc}") + raise GenerateIntendedConfigException("Error rendering Jinja template") from exc return Response( data={ "intended_config": intended_config, diff --git a/nautobot_golden_config/models.py b/nautobot_golden_config/models.py index 50fd9ef3..72b323db 100644 --- a/nautobot_golden_config/models.py +++ b/nautobot_golden_config/models.py @@ -633,6 +633,7 @@ def get_jinja_template_path_for_device(self, device): if self.jinja_repository is not None: rendered_path = render_jinja2(template_code=self.jinja_path_template, context={"obj": device}) return f"{self.jinja_repository.filesystem_path}{os.path.sep}{rendered_path}" + return None @extras_features( diff --git a/nautobot_golden_config/tests/test_api.py b/nautobot_golden_config/tests/test_api.py index 59f4d36e..7c79b9a2 100644 --- a/nautobot_golden_config/tests/test_api.py +++ b/nautobot_golden_config/tests/test_api.py @@ -435,17 +435,17 @@ def setUpTestData(cls): cls.git_repository = GitRepository.objects.get(name="test-jinja-repo-1") - def _setup_mock_path(self, MockPath): - MockPathInstance = MockPath.return_value - MockPathInstance.__str__.return_value = "test.j2" - MockPathInstance.read_text.return_value = r"Jinja test for device {{ name }}." - MockPathInstance.is_file.return_value = True - MockPathInstance.__truediv__.return_value = MockPathInstance # to handle Path('path') / 'file' - return MockPathInstance + def _setup_mock_path(self, MockPath): # pylint: disable=invalid-name + mock_path_instance = MockPath.return_value + mock_path_instance.__str__.return_value = "test.j2" + mock_path_instance.read_text.return_value = r"Jinja test for device {{ name }}." + mock_path_instance.is_file.return_value = True + mock_path_instance.__truediv__.return_value = mock_path_instance # to handle Path('path') / 'file' + return mock_path_instance @patch("nautobot_golden_config.api.views.ensure_git_repository") @patch("nautobot_golden_config.api.views.Path") - def test_generate_intended_config(self, MockPath, mock_ensure_git_repository): + def test_generate_intended_config(self, MockPath, mock_ensure_git_repository): # pylint: disable=invalid-name """Verify that the intended config is generated as expected.""" self.add_permissions("dcim.view_device") @@ -468,13 +468,13 @@ def test_generate_intended_config(self, MockPath, mock_ensure_git_repository): @patch("nautobot_golden_config.api.views.ensure_git_repository") @patch("nautobot_golden_config.api.views.Path") - def test_generate_intended_config_failures(self, MockPath, mock_ensure_git_repository): + def test_generate_intended_config_failures(self, MockPath, mock_ensure_git_repository): # pylint: disable=invalid-name """Verify that errors are handled as expected.""" self.add_permissions("dcim.view_device") self.add_permissions("extras.view_gitrepository") - MockPathInstance = self._setup_mock_path(MockPath) + mock_path_instance = self._setup_mock_path(MockPath) # test missing query parameters response = self.client.get( @@ -502,7 +502,7 @@ def test_generate_intended_config_failures(self, MockPath, mock_ensure_git_repos ) # test git repo not present on filesystem - MockPathInstance.is_file.return_value = False + mock_path_instance.is_file.return_value = False response = self.client.get( reverse("plugins-api:nautobot_golden_config-api:generate_intended_config"), @@ -519,8 +519,8 @@ def test_generate_intended_config_failures(self, MockPath, mock_ensure_git_repos ) # test invalid jinja template - MockPathInstance.is_file.return_value = True - MockPathInstance.read_text.return_value = r"Jinja test for device {{ name }." + mock_path_instance.is_file.return_value = True + mock_path_instance.read_text.return_value = r"Jinja test for device {{ name }." response = self.client.get( reverse("plugins-api:nautobot_golden_config-api:generate_intended_config"),