Skip to content

Commit

Permalink
Merge branch 'develop' into feature_delices_v2
Browse files Browse the repository at this point in the history
  • Loading branch information
abates committed Oct 1, 2024
2 parents 55355f7 + 4fc9ed7 commit 1f5f84f
Show file tree
Hide file tree
Showing 16 changed files with 1,494 additions and 1,090 deletions.
2 changes: 1 addition & 1 deletion docs/dev/extending.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Design builder is primarily extended by creating new action tags. These action t
## Action Tag Extensions

The action tags in Design Builder are provided by `design.Builder`. This component reads a design and then executes instructions that are specified in the design. Basic functions, provided out of the box, are
`create`, `create_or_update` and `update`. These actions are self explanatory (for details on syntax see [this document](../user//design_development.md#special-syntax)). Two additional actions are provided, these are the `ref` and `git_context` actions. These two actions are provided as extensions to the builder.
`get`, `create`, `create_or_update` and `update`. These actions are self explanatory (for details on syntax see [this document](../user//design_development.md#special-syntax)). Two additional actions are provided, these are the `ref` and `git_context` actions. These two actions are provided as extensions to the builder.

Extensions specify attribute and/or value actions to the object creator. Within a design template, these extensions can be used by specifying an exclamation point (!) followed by the extensions attribute or value tag. For instance, the `ref` extension implements both an attribute and a value extension. This extension can be used by specifying `!ref`. Extensions can add behavior to the object creator that is not supplied by the standard create and update actions.

Expand Down
4 changes: 2 additions & 2 deletions docs/user/design_development.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ For the remainder of this tutorial we will focus solely on the Design Job, Desig

## Design Components

Designs can be loaded either from local files or from a git repository. Either way, the structure of the actual designs and all the associated files is the same. Since, fundamentally, all designs are Nautobot Jobs, everything must be in a top level `jobs` python package (meaning the directory must contain the file `__init__.py`) and all design classes must be either defined in this `jobs` module or be imported to it. The following directory layout is from the [demo designs repository](hhttps://github.com/nautobot/demo-designs):
Designs can be loaded either from local files or from a git repository. Either way, the structure of the actual designs and all the associated files is the same. Since, fundamentally, all designs are Nautobot Jobs, everything must be in a top level `jobs` python package (meaning the directory must contain the file `__init__.py`) and all design classes must be either defined in this `jobs` module or be imported to it. The following directory layout is from the [demo designs repository](https://github.com/nautobot/demo-designs):

```bash
jobs
Expand Down Expand Up @@ -49,7 +49,7 @@ The `jobs` directory contains everything that is needed for implementing a desig

Within the `jobs` directory, the naming of modules and files is not important. However, it is recommended to use intuitive names to help understand each file's relationship with others. For instance, above there is are design contexts specified as both Python modules as well as YAML files and each design has exactly one design template. The relationship of context YAML files and context Python modules will be discussed later.

Designs are just specialized Nautobot jobs. Any design must inherit from `DesignJob`, and just like any other job, design jobs must be registered using `register_jbos`. An example design follows:
Designs are just specialized Nautobot jobs. Any design must inherit from `DesignJob`, and just like any other job, design jobs must be registered using `register_jobs`. An example design follows:

```python
from nautobot.apps.jobs import register_jobs
Expand Down
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ nav:
- "admin/release_notes/index.md"
- v1.0: "admin/release_notes/version_1.0.md"
- v1.1: "admin/release_notes/version_1.1.md"
- v2.0: "admin/release_notes/version_2.0.md"
- Developer Guide:
- Extending the App: "dev/extending.md"
- Contributing to the App: "dev/contributing.md"
Expand Down
39 changes: 20 additions & 19 deletions nautobot_design_builder/contrib/ext.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def _flatten(query: dict, prefix="") -> Iterator[Tuple[str, Any]]:
yield from LookupMixin._flatten(value, f"{prefix}{key}__")
else:
if isinstance(value, ModelInstance):
yield (f"!get:{prefix}{key}", value.instance)
yield (f"!get:{prefix}{key}", value.design_instance)
else:
yield (f"!get:{prefix}{key}", value)

Expand Down Expand Up @@ -134,7 +134,7 @@ def lookup(self, queryset, query, parent: ModelInstance = None):
try:
model_class = self.environment.model_class_index[queryset.model]
if parent:
return parent.create_child(model_class, query, queryset)
return parent.design_metadata.create_child(model_class, query, queryset)
return model_class(self.environment, query, queryset)
except ObjectDoesNotExist:
# pylint: disable=raise-missing-from
Expand Down Expand Up @@ -311,25 +311,26 @@ def attribute(self, *args, value=None, model_instance: ModelInstance = None) ->
def connect():
cable_attributes.update(
{
"!create_or_update:termination_a_id": model_instance.instance.id,
"!create_or_update:termination_a_id": model_instance.design_instance.id,
"!create_or_update:termination_a_type_id": ContentType.objects.get_for_model(
model_instance.instance
model_instance.design_instance
).id,
"!create_or_update:termination_b_id": remote_instance.instance.id,
"!create_or_update:termination_b_id": remote_instance.design_instance.id,
"!create_or_update:termination_b_type_id": ContentType.objects.get_for_model(
remote_instance.instance
remote_instance.design_instance
).id,
}
)

existing_cable = dcim.Cable.objects.filter(
Q(termination_a_id=model_instance.instance.id) | Q(termination_b_id=remote_instance.instance.id)
Q(termination_a_id=model_instance.design_instance.id)
| Q(termination_b_id=remote_instance.design_instance.id)
).first()
Cable = ModelInstance.factory(dcim.Cable) # pylint:disable=invalid-name
if existing_cable:
if (
existing_cable.termination_a_id != model_instance.instance.id
or existing_cable.termination_b_id != remote_instance.instance.id
existing_cable.termination_a_id != model_instance.design_instance.id
or existing_cable.termination_b_id != remote_instance.design_instance.id
):
self.environment.decommission_object(existing_cable.id, f"Cable {existing_cable.id}")
cable = Cable(self.environment, cable_attributes)
Expand Down Expand Up @@ -383,7 +384,7 @@ def attribute(self, *args, value: dict = None, model_instance: ModelInstance = N
identified_by = value.pop("identified_by", None)
if identified_by:
try:
model_instance.instance = model_instance.relationship_manager.get(**identified_by)
model_instance.design_instance = model_instance.relationship_manager.get(**identified_by)
return None
except ObjectDoesNotExist:
pass
Expand Down Expand Up @@ -499,7 +500,7 @@ def attribute(self, *args, value: dict = None, model_instance: "ModelInstance" =
if parent is None:
raise DesignImplementationError("the child_prefix tag requires a parent")
if isinstance(parent, ModelInstance):
parent = str(parent.instance.prefix)
parent = str(parent.design_instance.prefix)
elif not isinstance(parent, str):
raise DesignImplementationError("parent prefix must be either a previously created object or a string.")

Expand All @@ -511,8 +512,8 @@ def attribute(self, *args, value: dict = None, model_instance: "ModelInstance" =
attr = args[0] if args else "prefix"

if action:
model_instance.metadata.action = action
model_instance.metadata.filter[attr] = str(network_offset(parent, offset))
model_instance.design_metadata.action = action
model_instance.design_metadata.filter[attr] = str(network_offset(parent, offset))
return None
return attr, str(network_offset(parent, offset))

Expand Down Expand Up @@ -599,8 +600,8 @@ def attribute(self, *args, value=None, model_instance: ModelInstance = None) ->
peering_a = None
peering_z = None
try:
peering_a = endpoint_a.instance.peering
peering_z = endpoint_z.instance.peering
peering_a = endpoint_a.design_instance.peering
peering_z = endpoint_z.design_instance.peering
except self.Peering.model_class.DoesNotExist:
pass

Expand All @@ -615,13 +616,13 @@ def attribute(self, *args, value=None, model_instance: ModelInstance = None) ->
peering_z.delete()

retval["endpoints"] = [endpoint_a, endpoint_z]
endpoint_a.metadata.attributes["peering"] = model_instance
endpoint_z.metadata.attributes["peering"] = model_instance
endpoint_a.design_metadata.attributes["peering"] = model_instance
endpoint_z.design_metadata.attributes["peering"] = model_instance

def post_save():
peering_instance: ModelInstance = model_instance
endpoint_a = peering_instance.instance.endpoint_a
endpoint_z = peering_instance.instance.endpoint_z
endpoint_a = peering_instance.design_instance.endpoint_a
endpoint_z = peering_instance.design_instance.endpoint_z
endpoint_a.peer, endpoint_z.peer = endpoint_z, endpoint_a
endpoint_a.save()
endpoint_z.save()
Expand Down
16 changes: 8 additions & 8 deletions nautobot_design_builder/debug.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@

class ObjDetails: # noqa:D101 # pylint:disable=too-few-public-methods,missing-class-docstring
def __init__(self, obj): # noqa:D107 # pylint:disable=missing-function-docstring
self.instance = obj
if hasattr(obj, "instance"):
self.instance = obj.instance
self.design_instance = obj
if hasattr(obj, "design_instance"):
self.design_instance = obj.design_instance
try:
description = str(obj)
if description.startswith("<class"):
Expand All @@ -20,23 +20,23 @@ def __init__(self, obj): # noqa:D107 # pylint:disable=missing-function-docstri

self.obj = obj
self.obj_class = obj.__class__.__name__
self.obj_id = str(getattr(self.instance, "id", None))
if hasattr(self.instance, "name"):
self.name = getattr(self.instance, "name")
self.obj_id = str(getattr(self.design_instance, "id", None))
if hasattr(self.design_instance, "name"):
self.name = getattr(self.design_instance, "name")
else:
self.name = None
self.description = description

def __str__(self): # noqa:D105 # pylint:disable=missing-function-docstring
if isinstance(self.instance, models.Model):
if isinstance(self.design_instance, models.Model):
string = self.obj_class + " "
if self.name is not None:
string += '"' + self.name + '"' + ":"
elif self.description:
string += self.description + ":"
string += self.obj_id
return string
if isinstance(self.instance, dict):
if isinstance(self.design_instance, dict):
return str(self.obj)
return self.description or self.name or self.obj_class

Expand Down
Loading

0 comments on commit 1f5f84f

Please sign in to comment.