diffsync
diffsync copied to clipboard
Create an IGNORE_CASE flag to prevent case-sensitive mismatches
Environment
- DiffSync version: 1.4.1
Proposed Functionality
Implement either a global or model flag (or both) called IGNORE_CASE
, that will tell DiffSync to ignore case-sensitive mismatches.
Example for Global Flags:
from diffsync.enum import DiffSyncFlags
flags = DiffSyncFlags.IGNORE_CASE
diff = nautobot.diff_from(local, flags=flags)
Example for Model Flags:
from diffsync import DiffSync
from diffsync.enum import DiffSyncModelFlags
from model import MyDeviceModel
class MyAdapter(DiffSync):
device = MyDeviceModel
def load(self, data):
"""Load all devices into the adapter and add the flag IGNORE to all firewall devices."""
for device in data.get("devices"):
obj = self.device(name=device["name"])
if "firewall" in device["name"]:
obj.model_flags = DiffSyncModelFlags.IGNORE_CASE
self.add(obj)
Use Case
Currently, if we are trying to sync the same object from different backends that have the same name but without the same case (i.e.: "my-device" & "My-Device"), they will be marked as different, thus deleting the first device to replace it with the new one.
Below is an example to show the current limitations of not having such flag. As you can see from the DATA_BACKEND_A
and DATA_BACKEND_B
variables, the values are the same, but the first is in all caps, whereas the second is all lowercase.
from diffsync.logging import enable_console_logging
from diffsync import DiffSync
from diffsync import DiffSyncModel
class Site(DiffSyncModel):
_modelname = "site"
_identifiers = ("name",)
name: str
@classmethod
def create(cls, diffsync, ids, attrs):
print(f"Create {cls._modelname}")
return super().create(ids=ids, diffsync=diffsync, attrs=attrs)
def update(self, attrs):
print(f"Update {self._modelname}")
return super().update(attrs)
def delete(self):
print(f"Delete {self._modelname}")
super().delete()
return self
DATA_BACKEND_A = ["SITE-A"]
DATA_BACKEND_B = ["site-A"]
class BackendA(DiffSync):
site = Site
top_level = ["site"]
def load(self):
for site_name in DATA_BACKEND_A:
site = self.site(name=site_name)
self.add(site)
class BackendB(DiffSync):
site = Site
top_level = ["site"]
def load(self):
for site_name in DATA_BACKEND_B:
site = self.site(name=site_name)
self.add(site)
def main():
enable_console_logging(verbosity=0)
backend_a = BackendA(name="Backend-A")
backend_a.load()
backend_b = BackendB(name="Backend-B")
backend_b.load()
backend_a.sync_to(backend_b)
if __name__ == "__main__":
main()
Upon executing this script, the output is:
Create site
Delete site
So we are replacing an object that could potentially be the same.
The implementation of this flag could help mitigate unexpected results when the user knows he might have case-insensitive data from both backends, and remove the need to use functions such as .lower()
or .casefold()
each time he creates a new object.
Hi @antoinedelia, thank you for this proposal, personally I like the idea A global flag should be "easy" to implement, for the model flag that might be more complicated but why not.
@glennmatthews what do you think ?