Skip to content

Commit

Permalink
Remove remaining references to slugs in core models
Browse files Browse the repository at this point in the history
  • Loading branch information
jmcgill298 committed Sep 7, 2023
1 parent 571dd95 commit 0658338
Show file tree
Hide file tree
Showing 11 changed files with 77 additions and 88 deletions.
16 changes: 8 additions & 8 deletions docs/user/app_feature_config_postprocessing.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,18 @@ There are two different ways to customize the default behavior of `get_config_po

The `render_secrets` function performs an extra Jinja rendering on top of an intended configuration, exposing new custom Jinja filters:

- `get_secret_by_secret_group_slug`: as the name suggests, it returns the secret_group value, for a secret type, from its `slug`.
- `get_secret_by_secret_group_name`: as the name suggests, it returns the secret_group value, for a secret type, from its `name`.

!!! note
Other default Django or Netutils filters are not available in this Jinja environment. Only `encrypt_<vendor>_type5` and `encrypt_<vendor>_type7` can be used together with the `get_secret` filters.

Because this rendering is separated from the standard generation of the intended configuration, you must use the `{% raw %}` Jinja syntax to avoid being processed by the initial generation stage.

1. For example, an original template like this, `{% raw %}ppp pap sent-username {{ secrets_group["slug"] | get_secret_by_secret_group_slug("username")}}{% endraw %}`
2. Produces an intended configuration as `ppp pap sent-username {{ secrets_group["slug"] | get_secret_by_secret_group_slug("username") }}`
1. For example, an original template like this, `{% raw %}ppp pap sent-username {{ secrets_group["name"] | get_secret_by_secret_group_name("username")}}{% endraw %}`
2. Produces an intended configuration as `ppp pap sent-username {{ secrets_group["name"] | get_secret_by_secret_group_name("username") }}`
3. After the `render_secrets`, it becomes `ppp pap sent-username my_username`.

Notice that the `get_secret` filters take arguments. In the example, the `Secret_group` slug is passed, together with the type of the `Secret`. Check every signature for extra customization.
Notice that the `get_secret` filters take arguments. In the example, the `secret_group` name is passed, together with the type of the `Secret`. Check every signature for extra customization.

!!! note
Remember that to render these secrets, the user requesting it via UI or API, MUST have read permissions to Secrets Groups, Golden Config, and the specific Device object.
Expand All @@ -64,7 +64,7 @@ This shows how Render the Secrets feature for a `Device`, for the default `Secre
query ($device_id: ID!) {
device(id: $device_id) {
secrets_group {
slug
name
}
location {
rel_my_secret_relationship_for_location {
Expand All @@ -80,13 +80,13 @@ query ($device_id: ID!) {
Using the default `secrets_group` FK in `Device`:

```jinja2
{% raw %}{{ secrets_group["slug"] | get_secret_by_secret_group_slug("password") | default('no password') }}{% endraw %}
{% raw %}{{ secrets_group["name"] | get_secret_by_secret_group_name("password") | default('no password') }}{% endraw %}
```

Using the custom relationship at the `Location` level:

```jinja2
{% raw %}{{ location["rel_my_secret_relationship_for_location"][0]["slug"] | get_secret_by_secret_group_slug("password") | default('no password') }}{% endraw %}
{% raw %}{{ location["rel_my_secret_relationship_for_location"][0]["name"] | get_secret_by_secret_group_name("password") | default('no password') }}{% endraw %}
```

This will end up rendering the secret, of type "password", for the corresponding `SecretGroup`.
Expand All @@ -96,5 +96,5 @@ This will end up rendering the secret, of type "password", for the corresponding
Obviously, the rendering process can find multiple challenges, that are managed, and properly explained to take corrective actions:

```
Found an error rendering the configuration to push: Jinja encountered and UndefinedError: 'None' has no attribute 'slug', check the template for missing variable definitions.
Found an error rendering the configuration to push: Jinja encountered and UndefinedError: 'None' has no attribute 'name', check the template for missing variable definitions.
```
5 changes: 2 additions & 3 deletions docs/user/app_feature_sotagg.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,17 +92,16 @@ query ($device_id: ID!) {
}
tags {
name
slug
}
device_role {
role {
name
}
platform {
name
network_driver
manufacturer {
name
}
network_driver
napalm_driver
}
location {
Expand Down
10 changes: 5 additions & 5 deletions docs/user/app_getting_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ The files within these folders can follow any naming pattern or nested folder st
│ │ └── juniper_junos.yml
```

The `YAML` files will contain all the attributes necessary to identify an object (for instance, a `ComplianceRule` is identified by the `feature_slug` and the `platform_slug` together) and the other attributes (the ones that are not used to identify the object). For example:
The `YAML` files will contain all the attributes necessary to identify an object (for instance, a `ComplianceRule` is identified by the `feature_slug` and the `platform_name` together) and the other attributes (the ones that are not used to identify the object). For example:

`compliance_features` example:

Expand All @@ -169,7 +169,7 @@ The `YAML` files will contain all the attributes necessary to identify an object
```yaml
---
- feature_slug: "aaa"
platform_slug: "cisco_ios"
platform_name: "Cisco IOS"
config_ordered: true
match_config: |
aaa
Expand All @@ -184,7 +184,7 @@ The `YAML` files will contain all the attributes necessary to identify an object

```yaml
---
- platform_slug: "cisco_ios"
- platform_name: "Cisco IOS"
name: "Build config"
regex: '^Building\s+configuration.*\n'
```
Expand All @@ -194,7 +194,7 @@ The `YAML` files will contain all the attributes necessary to identify an object
```yaml
---
- name: "username"
platform_slug: "cisco_ios"
platform_name: "Cisco IOS"
description: "username"
regex: '(username\s+\S+\spassword\s+5\s+)\S+(\s+role\s+\S+)'
replace: '\1<redacted_config>\2'
Expand All @@ -213,7 +213,7 @@ CustomField data can be added using the `_custom_field_data` attribute, that tak
```

!!! note
For Foreign Key references to `ComplianceFeature` and `Platform` we use the keywords `feature_slug` and `platform_slug` respectively.
For Foreign Key references to `ComplianceFeature` and `Platform` we use the keywords `feature_slug` and `platform_name` respectively.

1. Add the Git repository that will be used to sync Git properties.

Expand Down
23 changes: 10 additions & 13 deletions docs/user/app_use_cases.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ To update existing settings click on one of the `Settings` name.
|Intended Repositories |The Git Repository where your intended configuration state files will be found. |
|Intended Path|A Jinja template which defines the path and name of intended configuration state files within the intended state repository. e.g. `{{obj.location.name}}/{{obj.name}}.intended_cfg`|
|Jinja Repository |The Git Repository where your jinja templates will be found. |
|Jinja Path|A Jinja template which defines the path (within the repository) and name of the Jinja template file. e.g. `{{obj.platform.slug}}/{{obj.device_role.slug}}/main.j2`|
|Jinja Path|A Jinja template which defines the path (within the repository) and name of the Jinja template file. e.g. `{{obj.platform.name}}/{{obj.role.name}}/main.j2`|
|Dynamic Group|The scope of devices on which Golden Config's jobs can operate. |
|GraphQL Query|A query that is evaluated and used to render the config. The query must start with `query ($device_id: ID!)`.|

Expand All @@ -78,31 +78,28 @@ Within the Detail view of a Golden Config Setting the section to denote the scop

The below configurations of scope can either be removed or specified for pre 1.2 only, the same logic applies in 1.2 and onwards but via DynamicGroups.

Filtering to specific platforms, based on their slug.
Filtering to specific platforms, based on their name.

```json
{
"platform": [
"cisco_ios",
"cisco_nxos",
"arista_eos",
"juniper_junos"
"Cisco IOS",
"Cisco NXOS",
"Arista EOS",
"Juniper Junos"
]
}
```

!!! note
The Platform slug is an important value, see the [FAQ](./app_faq.md) for further details.

Adding a "has_primary_ip" check.

```json
{
"platform": [
"cisco_ios",
"cisco_nxos",
"arista_eos",
"juniper_junos"
"Cisco IOS",
"Cisco NXOS",
"Arista EOS",
"Juniper Junos"
],
"has_primary_ip": "True"
}
Expand Down
22 changes: 11 additions & 11 deletions nautobot_golden_config/datasources.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,23 +70,23 @@ def refresh_git_gc_properties(repository_record, job_result, delete=False): # p
"class": ComplianceRule,
"id_keys": (
("feature", "feature_slug"),
("platform", "platform_slug"),
("platform", "platform_name"),
),
},
{
"directory_name": "config_removes",
"class": ConfigRemove,
"id_keys": (
("name", "name"),
("platform", "platform_slug"),
("platform", "platform_name"),
),
},
{
"directory_name": "config_replaces",
"class": ConfigReplace,
"id_keys": (
("name", "name"),
("platform", "platform_slug"),
("platform", "platform_name"),
),
},
)
Expand All @@ -102,29 +102,29 @@ def refresh_git_gc_properties(repository_record, job_result, delete=False): # p

def get_id_kwargs(gc_config_item_dict, id_keys, job_result):
"""Method to get the proper id kwargs and remove them from gc_config_item_dict."""
# fk_slug_class_mapping contains a mapping of the FK attributes to the related model
fk_slug_class_mapping = {"feature": ComplianceFeature, "platform": Platform}
# fk_class_mapping contains a mapping of the FK attributes to the related model
fk_class_mapping = {"feature": ComplianceFeature, "platform": Platform}

id_kwargs = {}
for id_key in id_keys:
actual_attr_name = id_key[0]
yaml_attr_name = id_key[1]

# If the attribute is actually a FK reference, we need to resolve the related object
if actual_attr_name in fk_slug_class_mapping:
if actual_attr_name in fk_class_mapping:
_, field_name = yaml_attr_name.split("_")
kwargs = {field_name: gc_config_item_dict[yaml_attr_name]}
try:
id_kwargs[actual_attr_name] = fk_slug_class_mapping[actual_attr_name].objects.get(
slug=gc_config_item_dict[yaml_attr_name]
)
except fk_slug_class_mapping[actual_attr_name].DoesNotExist:
id_kwargs[actual_attr_name] = fk_class_mapping[actual_attr_name].objects.get(**kwargs)
except fk_class_mapping[actual_attr_name].DoesNotExist:
job_result.log(
(
f"Reference to {yaml_attr_name}: {gc_config_item_dict[yaml_attr_name]}",
"is not available.",
),
level_choice=LogLevelChoices.LOG_WARNING,
)
raise MissingReference from fk_slug_class_mapping[actual_attr_name].DoesNotExist
raise MissingReference from fk_class_mapping[actual_attr_name].DoesNotExist
else:
id_kwargs[actual_attr_name] = gc_config_item_dict[yaml_attr_name]

Expand Down
4 changes: 2 additions & 2 deletions nautobot_golden_config/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ class ComplianceRuleFilterForm(NautobotFilterForm):

q = forms.CharField(required=False, label="Search")
platform = core_forms.DynamicModelMultipleChoiceField(
queryset=Platform.objects.all(), to_field_name="slug", required=False, null_option="None"
queryset=Platform.objects.all(), to_field_name="name", required=False, null_option="None"
)

feature = core_forms.DynamicModelMultipleChoiceField(
Expand Down Expand Up @@ -281,7 +281,7 @@ class ConfigReplaceFilterForm(NautobotFilterForm):
model = models.ConfigReplace

platform = core_forms.DynamicModelMultipleChoiceField(
queryset=Platform.objects.all(), to_field_name="slug", required=False, null_option="None"
queryset=Platform.objects.all(), to_field_name="name", required=False, null_option="None"
)
name = core_forms.DynamicModelChoiceField(
queryset=models.ConfigReplace.objects.all(), to_field_name="name", required=False
Expand Down
9 changes: 4 additions & 5 deletions nautobot_golden_config/tests/test_datasources.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
from nautobot_golden_config.datasources import get_id_kwargs, MissingReference


@unittest.skip("TODO: Fix datasources.get_id_kwargs to not use slugs")
class GitPropertiesDatasourceTestCase(TestCase):
"""Test Git GC Properties datasource."""

Expand Down Expand Up @@ -54,20 +53,20 @@ def test_get_id_kwargs_3(self):

def test_get_id_kwargs_4(self):
"""Test simple get_id_kwargs ."""
gc_config_item_dict = {"platform_slug": "invalid_platform"}
gc_config_item_dict = {"platform_name": "invalid_platform"}
with self.assertRaises(MissingReference):
get_id_kwargs(
gc_config_item_dict,
(("platform", "platform_slug"),),
(("platform", "platform_name"),),
self.job_result,
)

def test_get_id_kwargs_5(self):
"""Test simple get_id_kwargs 5."""
gc_config_item_dict = {"platform_slug": "example_platform"}
gc_config_item_dict = {"platform_name": "example_platform"}
id_kwargs = get_id_kwargs(
gc_config_item_dict,
(("platform", "platform_slug"),),
(("platform", "platform_name"),),
self.job_result,
)
self.assertEqual(id_kwargs, {"platform": self.platform})
Expand Down
Loading

0 comments on commit 0658338

Please sign in to comment.