devaci 1.0.0__tar.gz → 1.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. {devaci-1.0.0 → devaci-1.0.1}/PKG-INFO +1 -1
  2. {devaci-1.0.0 → devaci-1.0.1}/pyproject.toml +1 -1
  3. {devaci-1.0.0 → devaci-1.0.1}/src/devaci/cobra/__init__.py +6 -2
  4. {devaci-1.0.0 → devaci-1.0.1}/src/devaci/deploy.py +1 -1
  5. {devaci-1.0.0 → devaci-1.0.1}/src/devaci/jinja.py +2 -4
  6. {devaci-1.0.0 → devaci-1.0.1}/src/devaci/results.py +7 -3
  7. {devaci-1.0.0 → devaci-1.0.1}/tests/test_deploy.py +13 -0
  8. {devaci-1.0.0 → devaci-1.0.1}/tests/test_jinja.py +10 -4
  9. {devaci-1.0.0 → devaci-1.0.1}/.gitignore +0 -0
  10. {devaci-1.0.0 → devaci-1.0.1}/LICENSE +0 -0
  11. {devaci-1.0.0 → devaci-1.0.1}/README.md +0 -0
  12. {devaci-1.0.0 → devaci-1.0.1}/src/devaci/__init__.py +0 -0
  13. {devaci-1.0.0 → devaci-1.0.1}/src/devaci/cobra/base.py +0 -0
  14. {devaci-1.0.0 → devaci-1.0.1}/src/devaci/cobra/builders.py +0 -0
  15. {devaci-1.0.0 → devaci-1.0.1}/src/devaci/cobra/registry.py +0 -0
  16. {devaci-1.0.0 → devaci-1.0.1}/src/devaci/console.py +0 -0
  17. {devaci-1.0.0 → devaci-1.0.1}/src/devaci/data.py +0 -0
  18. {devaci-1.0.0 → devaci-1.0.1}/src/devaci/exceptions.py +0 -0
  19. {devaci-1.0.0 → devaci-1.0.1}/src/devaci/filters.py +0 -0
  20. {devaci-1.0.0 → devaci-1.0.1}/src/devaci/py.typed +0 -0
  21. {devaci-1.0.0 → devaci-1.0.1}/tests/__init__.py +0 -0
  22. {devaci-1.0.0 → devaci-1.0.1}/tests/test_cobra.py +0 -0
  23. {devaci-1.0.0 → devaci-1.0.1}/tests/test_data.py +0 -0
  24. {devaci-1.0.0 → devaci-1.0.1}/tests/test_devaci.py +0 -0
  25. {devaci-1.0.0 → devaci-1.0.1}/tests/test_filters.py +0 -0
  26. {devaci-1.0.0 → devaci-1.0.1}/tests/test_results.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: devaci
3
- Version: 1.0.0
3
+ Version: 1.0.1
4
4
  Summary: Python library that generates Cisco ACI configuration and optionally pushes it to an APIC controller via Cisco's official Cobra SDK.
5
5
  Project-URL: Homepage, https://github.com/cocuni80/devaci
6
6
  Project-URL: Repository, https://github.com/cocuni80/devaci
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "devaci"
7
- version = "1.0.0"
7
+ version = "1.0.1"
8
8
  description = "Python library that generates Cisco ACI configuration and optionally pushes it to an APIC controller via Cisco's official Cobra SDK."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -40,12 +40,16 @@ class CobraBuilder:
40
40
 
41
41
  @property
42
42
  def xml(self) -> str | None:
43
- """Return the rendered XML payload."""
43
+ """Return the rendered XML payload, or None when the config is empty."""
44
+ if not self.config.configMos:
45
+ return None
44
46
  return cast(str | None, self.config.xmldata)
45
47
 
46
48
  @property
47
49
  def json(self) -> Any:
48
- """Return the rendered JSON payload as a dict."""
50
+ """Return the rendered JSON payload as a dict, or None when the config is empty."""
51
+ if not self.config.configMos:
52
+ return None
49
53
  return json.loads(self.config.data)
50
54
 
51
55
  def render(self, output: dict[str, Any]) -> CobraResult:
@@ -120,7 +120,7 @@ class DeployClass:
120
120
 
121
121
  try:
122
122
  renderer = JinjaRenderer()
123
- output = renderer.render(content, name=name, **self._variables)
123
+ output = renderer.render(content, **self._variables)
124
124
  logs.append(f"[Jinja]: Template {name} was rendered successfully.")
125
125
  logger.info(logs[-1])
126
126
 
@@ -26,7 +26,7 @@ class JinjaRenderer:
26
26
  self._env.filters["range"] = range_filter
27
27
  self._env.filters["nan"] = nan_filter
28
28
 
29
- def render(self, template: str, name: str | None = None, **variables: Any) -> dict[str, Any]:
29
+ def render(self, template: str, **variables: Any) -> dict[str, Any]:
30
30
  """Render ``template`` with ``variables`` and parse the YAML output."""
31
31
  try:
32
32
  rendered = self._env.from_string(template).render(**variables)
@@ -36,7 +36,5 @@ class JinjaRenderer:
36
36
 
37
37
  output = load_yaml(rendered)
38
38
  if not isinstance(output, dict):
39
- raise JinjaError(
40
- f"[Jinja] -> [ConfigError]: Template {name} did not produce a mapping."
41
- )
39
+ raise JinjaError("[Jinja] -> [ConfigError]: Template did not produce a mapping.")
42
40
  return output
@@ -5,7 +5,7 @@ from __future__ import annotations
5
5
  import json
6
6
  from dataclasses import dataclass, field
7
7
  from datetime import datetime
8
- from typing import Any
8
+ from typing import Any, cast
9
9
 
10
10
 
11
11
  def _timestamp() -> str:
@@ -44,11 +44,15 @@ class CobraResult(Result):
44
44
 
45
45
  @property
46
46
  def xml(self) -> str | None:
47
- return self.config.xmldata if self.config is not None else None
47
+ if self.config is None or not self.config.configMos:
48
+ return None
49
+ return cast(str | None, self.config.xmldata)
48
50
 
49
51
  @property
50
52
  def json(self) -> Any:
51
- return json.loads(self.config.data) if self.config is not None else None
53
+ if self.config is None or not self.config.configMos:
54
+ return None
55
+ return json.loads(self.config.data)
52
56
 
53
57
  def to_dict(self) -> dict[str, Any]:
54
58
  data = super().to_dict()
@@ -83,3 +83,16 @@ def test_deploy_save_output(tmp_path):
83
83
  output = tmp_path / "scripts" / "config.xml"
84
84
  assert output.exists()
85
85
  assert "acme" in output.read_text(encoding="utf-8")
86
+
87
+
88
+ def test_deploy_variable_named_name(tmp_path):
89
+ aci = DeployClass(testing=True, working_folder=tmp_path, logging=False)
90
+ aci.variables = {"name": "test", "descr": "Test tenant"}
91
+ aci.template = (
92
+ "fvTenant:\n - name: {{ name }}\n descr: {{ descr }}\n",
93
+ "tenant.j2",
94
+ )
95
+ aci.deploy()
96
+
97
+ assert aci.results[0]["success"] is True
98
+ assert "test" in aci.config
@@ -6,23 +6,29 @@ from devaci.jinja import JinjaRenderer
6
6
 
7
7
  def test_render_mapping():
8
8
  renderer = JinjaRenderer()
9
- out = renderer.render("fvTenant:\n - name: acme\n", name="t.j2")
9
+ out = renderer.render("fvTenant:\n - name: acme\n")
10
10
  assert out == {"fvTenant": [{"name": "acme"}]}
11
11
 
12
12
 
13
13
  def test_render_with_variables():
14
14
  renderer = JinjaRenderer()
15
- out = renderer.render("fvTenant:\n - name: {{ tenant }}\n", name="t.j2", tenant="acme")
15
+ out = renderer.render("fvTenant:\n - name: {{ tenant }}\n", tenant="acme")
16
+ assert out == {"fvTenant": [{"name": "acme"}]}
17
+
18
+
19
+ def test_render_variable_named_name():
20
+ renderer = JinjaRenderer()
21
+ out = renderer.render("fvTenant:\n - name: {{ name }}\n", name="acme")
16
22
  assert out == {"fvTenant": [{"name": "acme"}]}
17
23
 
18
24
 
19
25
  def test_render_syntax_error():
20
26
  renderer = JinjaRenderer()
21
27
  with pytest.raises(JinjaError):
22
- renderer.render("{% if %}", name="t.j2")
28
+ renderer.render("{% if %}")
23
29
 
24
30
 
25
31
  def test_render_non_mapping():
26
32
  renderer = JinjaRenderer()
27
33
  with pytest.raises(JinjaError):
28
- renderer.render("- a\n- b\n", name="t.j2")
34
+ renderer.render("- a\n- b\n")
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes