Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ltm-1.6] Import mode for existing data #178

Closed
wants to merge 5 commits into from
Closed

Conversation

chadell
Copy link
Contributor

@chadell chadell commented Jul 5, 2024

This PR adds a new feature to import existing data into a new Design Deployment

TODOs:

  • Check why identifiers are not included in the imported change record
  • Include more actions other than "create_and_update", cover the "create" and "update" (for attributes only, not full control)
  • Create a queryset used many times
  • Add Docs for the new functionality
  • Add a functionality to remove traceability when decommissioning a design deployment

@chadell chadell requested review from abates and mzbroch as code owners July 5, 2024 09:28
@chadell chadell force-pushed the 1.6_import_state branch from ce4f519 to 800b691 Compare July 9, 2024 09:34
Comment on lines +569 to +571
if self.environment.import_mode and self.metadata.action != ModelMetadata.UPDATE:
self.metadata.attributes.update(field_values)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the circumstances that would cause this block of code to be executed? If the builder is not running in import mode, then this block is reached from a create_or_update when the requested object already exists and will now be updated. With this change I think we essentially treat all create actions as create_or_update when in import mode, but I doubt that there is even a query filter for most cases of create. If a design is intended to be able to be run in import mode then there shouldn't be any actions that are create only, they should be create_or_update in order to identify the query fields.

@@ -842,7 +852,7 @@ def resolve_values(self, value: Union[list, dict, str]) -> Any:
value[k] = self.resolve_value(item)
return value

def _create_objects(self, model_class: Type[ModelInstance], objects: Union[List[Any], Dict[str, Any]]):
def _create_or_import_objects(self, model_class: Type[ModelInstance], objects: Union[List[Any], Dict[str, Any]]):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

while this method is probably poorly named, I'm not sure that this PR should be where we rename it. Every change we have in the ltm branch is something we'll have to figure out how to pull over to the develop branch (which still doesn't have the lifecycle changes in it). I'd prefer to keep changes to absolute minimums in order to make the updates to develop easier down the road.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a valid point. I will exchange it by a comment about it

@@ -282,6 +294,7 @@ def _run_in_transaction(self, **kwargs): # pylint: disable=too-many-branches, t

self.job_result.job_kwargs = {"data": self.serialize_data(data)}

data["import_mode"] = self.determine_import_mode(data)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
data["import_mode"] = self.determine_import_mode(data)
data["import_mode"] = self.is_deployment_job() and data.get("import_mode", False)

Comment on lines +84 to +95
@classmethod
def determine_import_mode(cls, data):
"""Determine the import mode, if specified."""
if not cls.is_deployment_job():
return None

if "import_mode" not in data:
return False
# TODO: not sure why not got the default to False from _get_vars
# raise DesignImplementationError("No import mode was provided for the deployment.")
return data["import_mode"]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@classmethod
def determine_import_mode(cls, data):
"""Determine the import mode, if specified."""
if not cls.is_deployment_job():
return None
if "import_mode" not in data:
return False
# TODO: not sure why not got the default to False from _get_vars
# raise DesignImplementationError("No import mode was provided for the deployment.")
return data["import_mode"]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought it would be clearer to warp in this function similar to what we had for deployment name...

Comment on lines +309 to +311
if data["import_mode"] and data["deployment_name"]:
self.log_info(message=f'Running in import mode for {data["deployment_name"]}.')

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if data["import_mode"] and data["deployment_name"]:
self.log_info(message=f'Running in import mode for {data["deployment_name"]}.')
if data["import_mode"]:
self.log_info(message=f'Running in import mode for {data["deployment_name"]}.')

The second expression in the conditional is unneeded because import mode cannot be True if the design is not a deployment job.

@abates
Copy link
Contributor

abates commented Jul 24, 2024

Before I can approve this I'd like to see a test (or confirm that there is a test) that does the following:

  1. Creates a bunch of objects outside of a design
  2. Runs a design in import mode that will create some objects that were created already in step 1. The portions of the design that correspond to step 1 should have no action tags (default create).
  3. Confirms that there are no errors and that the objects that should have been imported now show as "owned" by the design.

@@ -27,14 +31,29 @@ class Meta: # pylint: disable=too-few-public-methods
def run(self, data, commit):
"""Execute Decommissioning job."""
deployments = data["deployments"]
only_traceability = data["only_traceability"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need a different name than only_traceability. It is not clear what the meaning of that variable is without really digging into the code. I'll give this some thought.

@@ -368,11 +373,56 @@ def log(self, model_instance):
entry.changes.update(model_instance.metadata.changes)
entry.save()
except ChangeRecord.DoesNotExist:
# Default full_control for created objects
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a detailed code comment here explaining this change. It seems complex and I'm not sure what the intent is.

@chadell
Copy link
Contributor Author

chadell commented Jul 25, 2024

Before I can approve this I'd like to see a test (or confirm that there is a test) that does the following:

  1. Creates a bunch of objects outside of a design
  2. Runs a design in import mode that will create some objects that were created already in step 1. The portions of the design that correspond to step 1 should have no action tags (default create).
  3. Confirms that there are no errors and that the objects that should have been imported now show as "owned" by the design.

This test https://github.com/nautobot/nautobot-app-design-builder/pull/178/files#diff-a45426cea1233c01ee8f3f6db38123c56701edf1f027f129206094df76415bffR124 does it but it's not "a bunch" but just one object. Do you think is relevant to have more than one?

@Kircheneer
Copy link
Contributor

Further open points

  •  Improve error handling for already claimed items on import mode (no exception directly bubbled up to job context)

@Kircheneer
Copy link
Contributor

Closing in favor of #181

@Kircheneer Kircheneer closed this Jul 31, 2024
@abates abates deleted the 1.6_import_state branch October 4, 2024 12:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants