diff --git a/README.md b/README.md index b287c9fae..67f732988 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,10 @@ This Nautobot application framework includes the following integrations: Read more about integrations [here](https://docs.nautobot.com/projects/ssot/en/latest/user/integrations). To enable and configure integrations follow the instructions from [the install guide](https://docs.nautobot.com/projects/ssot/en/latest/admin/install/#integrations-configuration). +### Building Your Own Integration + +If you need an integration that is not supported, you can build your own! Check out the documentation [here](https://docs.nautobot.com/projects/ssot/en/latest/dev/jobs). + ### Screenshots --- diff --git a/changes/468.documentation b/changes/468.documentation new file mode 100644 index 000000000..adff1b0e6 --- /dev/null +++ b/changes/468.documentation @@ -0,0 +1 @@ +Overhauled developer documentation structure. \ No newline at end of file diff --git a/changes/468.fixed b/changes/468.fixed new file mode 100644 index 000000000..cc4622c03 --- /dev/null +++ b/changes/468.fixed @@ -0,0 +1 @@ +Fixed SSoT jobs only allowing dry run unless you overwrote the `run` method. \ No newline at end of file diff --git a/docs/dev/issues.md b/docs/dev/issues.md index e69de29bb..ed6af5bb4 100644 --- a/docs/dev/issues.md +++ b/docs/dev/issues.md @@ -0,0 +1,21 @@ +# Reference: Common Issues and Solutions + +This pages describes common issues when implementing SSoT integrations and their respective solutions. + +## Converting Types Between Database and Pydantic + +Developers are able to override the default loading of basic parameters to control how that parameter is loaded from Nautobot. + +This only works with basic parameters belonging to the model and does not override more complex parameters (foreign keys, custom fields, custom relationships, etc.). + +To override a parameter, add a method with the name `load_param_{param_key}` to your adapter class inheriting from `NautobotAdapter`: + +```python +from nautobot_ssot.contrib import NautobotAdapter + +class YourSSoTNautobotAdapter(NautobotAdapter): + ... + def load_param_time_zone(self, parameter_name, database_object): + """Custom loader for `time_zone` parameter.""" + return str(getattr(database_object, parameter_name)) +``` diff --git a/docs/dev/jobs.md b/docs/dev/jobs.md index efab418e5..0d8afd4fc 100644 --- a/docs/dev/jobs.md +++ b/docs/dev/jobs.md @@ -1,180 +1,144 @@ -# Developing Data Source and Data Target Jobs +# Tutorial: Developing a Data Source Integration -A goal of this Nautobot app is to make it relatively quick and straightforward to develop and integrate your own system-specific Data Sources and Data Targets into Nautobot with a common UI and user experience. +This tutorial will walk you through building a custom data source (i.e. synchronizing data _to_ Nautobot) SSoT integration with a remote system. We will be using static data as the remote system, but it should be easy enough to substitute this later for any external systemy ou want to integrate with. -Familiarity with [DiffSync](https://diffsync.readthedocs.io/en/latest/) and with developing [Nautobot Jobs](https://nautobot.readthedocs.io/en/latest/additional-features/jobs/) is recommended. +*Familiarity with [DiffSync](https://diffsync.readthedocs.io/en/latest/) and with developing [Nautobot Jobs](https://nautobot.readthedocs.io/en/latest/additional-features/jobs/) is a plus, but important concepts will be explained or linked to along the way.** -## Quickstart Example +## Creating a New Nautobot App -The following code presents a minimum viable example for syncing VLANs from a remote system into Nautobot. You will have to adapt the following things to make this work for your use case: +To start building your own SSoT integration, you first need a project as well as the accompanying development environment. Network to Code provides the [Cookiecutter Nautobot App](https://github.com/nautobot/cookiecutter-nautobot-app) to make this as easy as possible for you. Check out the README of that project and bake a `nautobot-app` cookie. -- Swap out any mention of a "remote system" for your actual remote system, such as your IPAM tool -- Implement any models that you need, unless you are actually only interested in VLAN data -- Your `load` function in the remote adapter will probably be a little harder to implement, check the example integrations in this repository (under `nautobot_ssot/integrations`) +!!! note + The `nautobot-app-ssot` cookie is not yet updated to support the newest paradigms of building SSoT integrations, which is why it is recommended to instead use the `nautobot-app` cookie. + +Resume this tutorial whenever you have a folder on your development device with an up and running development environment. + +## Building the model -It contains 3 steps: +To synchronize data from a remote system into Nautobot, you need to first write a common model to bridge between the two systems. This model will be a [Pydantic](https://docs.pydantic.dev/latest/) model with built-in SSoT intelligence on top, specifically a subclass of `nautobot_ssot.contrib.NautobotModel`. -- Data modeling -- Adapters - - Nautobot - - Remote -- Nautobot Job +The following code snippet shows a basic VLAN model that includes the `name`, `vid` and `description` fields for the synchronization: ```python -# example_ssot_app/jobs.py +# tutorial_ssot_app/ssot/models.py from typing import Optional -from diffsync import DiffSync from nautobot.ipam.models import VLAN -from nautobot.extras.jobs import Job -from nautobot_ssot.contrib import NautobotModel, NautobotAdapter -from nautobot_ssot.jobs import DataSource -from remote_system import APIClient # This is only an example +from nautobot_ssot.contrib import NautobotModel -# Step 1 - data modeling class VLANModel(NautobotModel): - """DiffSync model for VLANs.""" - _model = VLAN - _modelname = "vlan" - _identifiers = ("vid", "group__name") - _attributes = ("description",) - + _model = VLAN # SSoT models need to have a 1-to-1 mapping with a concrete Nautobot model + _modelname = "vlan" # This is the model name diffsync uses for its logging output + _identifiers = ("name",) # These are the fields that uniquely identify a model instance + _attributes = ("vid", "status__name", "description",) # The rest of the data fields go here + + # The field names here need to match those in the Nautobot model exactly, otherwise it doesn't work. + name: str vid: int - group__name: Optional[str] = None description: Optional[str] = None - -# Step 2.1 - the Nautobot adapter -class MySSoTNautobotAdapter(NautobotAdapter): - """DiffSync adapter for Nautobot.""" - vlan = VLANModel - top_level = ("vlan",) - -# Step 2.2 - the remote adapter -class MySSoTRemoteAdapter(DiffSync): - """DiffSync adapter for remote system.""" - vlan = VLANModel - top_level = ("vlan",) - def __init__(self, *args, api_client, **kwargs): - super().__init__(*args, **kwargs) - self.api_client = api_client - - def load(self): - for vlan in self.api_client.get_vlans(): - loaded_vlan = self.vlan(vid=vlan["vlan_id"], group__name=vlan["grouping"], description=vlan["description"]) - self.add(loaded_vlan) - -# Step 3 - the job -class ExampleDataSource(DataSource, Job): - """SSoT Job class.""" - class Meta: - name = "Example Data Source" - - def load_source_adapter(self): - self.source_adapter = MySSoTRemoteAdapter(api_client=APIClient()) - self.source_adapter.load() - - def load_target_adapter(self): - self.target_adapter = MySSoTNautobotAdapter() - self.target_adapter.load() - -jobs = [ExampleDataSource] + # This is a foreign key to the `extras.status` model - its instances can be uniquely identified through its `name` + # field, which is why we specify this here + status__name: str ``` !!! note - This example is able to be so brief because usage of the `NautobotModel` class provides `create`, `update` and `delete` out of the box for the `VLANModel`. If you want to sync data _from_ Nautobot to another remote system, you will need to implement those yourself using whatever client or SDK provides the ability to write to that system. For examples, check out the existing integrations under `nautobot_ssot/integrations` + This example is able to be so brief because usage of the `NautobotModel` class provides `create`, `update` and `delete` out of the box for the `VLANModel`. If you want to sync data _from_ Nautobot _to_ another remote system, you will need to implement those yourself using whatever client or SDK provides the ability to write to that system. For examples, check out the existing integrations under `nautobot_ssot/integrations`. -The following sections describe the individual steps in more detail. -### Step 1 - Defining the Models +## Building the Adapters -The models use DiffSync, which in turn uses [Pydantic](https://docs.pydantic.dev/latest/) for its data modeling. Nautobot SSoT comes with a set of classes in `nautobot_ssot.contrib` that implement a lot of functionality for you automatically, provided you adhere to a set of rules. +On its own, the model is not particularly interesting. We have to combine it with an adapter to make it do interesting things. The adapter is responsible for pulling data out of Nautobot or your remote system (which is also Nautobot if you're following this tutorial). -The first rule is to define your models tightly after the Nautobot models themselves. Example: +### Building the Local adapter + +First, we define the adapter to load data from the local Nautobot instance - this is very straight-forward. ```python -from nautobot.tenancy.models import Tenant -from nautobot_ssot.contrib import NautobotModel +# tutorial_ssot_app/ssot/adapters.py +from nautobot_ssot.contrib import NautobotAdapter +from tutorial_ssot_app.ssot.models import VLANModel -class DiffSyncTenant(NautobotModel): - """An example model of a tenant.""" - _model = Tenant - _modelname = "tenant" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: str +class MySSoTNautobotAdapter(NautobotAdapter): + vlan = VLANModel # Map your model into the adapter + top_level = ("vlan",) # Tell the adapter to consider your model ``` -As you can see when looking at the [source code](https://github.com/nautobot/nautobot/blob/develop/nautobot/tenancy/models.py#L81) of the `nautobot.tenancy.models.Tenant` model, the fields on this model (`name` and `description`) match the names of the fields on the Nautobot model exactly. This enables the base class `NautobotModel` to dynamically load, create, update and delete your tenants without you needing to implement this functionality yourself. - -The above example shows the simplest field type (an attribute on the model), however, to build a production implementation you will need to understand how to identify different variants of fields by following the [modeling docs](../user/modeling.md). - -!!! warning - Currently, only normal fields, forwards foreign key fields and custom fields may be used in identifiers. Anything else is unsupported and will likely fail in unintuitive ways. - -### Step 2.1 - Creating the Nautobot Adapter - -Having created all your models, creating the Nautobot side adapter is very straight-forward: +Now create a couple of VLANs in the web GUI of Nautobot so that the adapter has some data to load. Once you're done, you can try out the adapter as follows: ```python -from nautobot_ssot.contrib import NautobotAdapter +from tutorial_ssot_app.ssot.adapters import MySSoTNautobotAdapter -from your_ssot_app.models import DiffSyncDevice, DiffSyncPrefix, DiffSyncIPAddress +adapter = MySSoTNautobotAdapter(job=None) +adapter.load() +adapter.count("vlan") # This will return the amount of VLANs that were loaded from Nautobot - -class YourSSoTNautobotAdapter(NautobotAdapter): - top_level = ("device", "prefix") - - device = DiffSyncDevice - prefix = DiffSyncPrefix - ip_address = DiffSyncIPAddress # Not in the `top_level` tuple, since it's a child of the prefix model +test_vlan = adapter.get_all("vlan")[0] # Now we retrieve an example VLAN to test the model with +test_vlan.update(attrs={"description": "My updated description"}) # Verify the update has worked using the web GUI +test_vlan.delete() # Verify the deletion has worked using the web GUI ``` -The `load` function is already implemented on this adapter and will automatically and recursively traverse any children relationships for you, provided the models are [defined correctly](../user/modeling.md). +### Building the Remote Adapter -Developers are able to override the default loading of basic parameters to control how that parameter is loaded from Nautobot. +To synchronize a diff, we need to pull data from both sides. Therefore, we now need to build the remote adapter. In this example we are reading static data, but feel free to substitute this adapter with your remote system of choice. -This only works with basic parameters belonging to the model and does not override more complex parameters (foreign keys, custom fields, custom relationships, etc.). - -To override a parameter, simply add a method with the name `load_param_{param_key}` to your adapter class inheriting from `NautobotAdapter`: +!!! note + Note that we are not subclassing from `nautobot_ssot.contrib.NautobotAdapter` here as we don't want the SSoT framework to auto-derive the loading implementation of the adapter for us, but rather want to handle this ourselves. ```python -from nautobot_ssot.contrib import NautobotAdapter - -class YourSSoTNautobotAdapter(NautobotAdapter): - ... - def load_param_time_zone(self, parameter_name, database_object): - """Custom loader for `time_zone` parameter.""" - return str(getattr(database_object, parameter_name)) -``` - -### Step 2.2 - Creating the Remote Adapter +# tutorial_ssot_app/ssot/adapters.py +from diffsync import DiffSync -Regardless of which direction you are synchronizing data in, you need to write the `load` method for the remote adapter yourself. You can find many examples of how this can be done in the `nautobot_ssot.integrations` module, which contains pre-existing integrations with remote systems. +from tutorial_ssot_app.ssot.models import VLANModel -!!! note - As the features in `nautobot_ssot.contrib` are still very new, most existing integrations do not use them. The example job in `nautobot_ssot/jobs/examples.py` can serve as a fully working example. +class MySSoTRemoteAdapter(DiffSync): + """DiffSync adapter for remote system.""" + vlan = VLANModel + top_level = ("vlan",) + + def load(self): + vlans = [ + {"name": "Servers", "vid": 100, "description": "Server VLAN Datacenter", "status__name": "Active"}, + {"name": "Printers", "vid": 200, "description": "Printer VLAN Office", "status__name": "Deprecated"}, + {"name": "Clients", "vid": 300, "description": "Client VLAN Office", "status__name": "Active"}, + ] + for vlan in vlans: + loaded_vlan = self.vlan( + name=vlan["name"], + vid=vlan["vid"], + description=vlan["description"], + status__name=vlan["status__name"] + ) + self.add(loaded_vlan) +``` -### Step 3 - The Job +Now you can verify that this adapter is working in a similar manner to how we did with the Nautobot adapter, feel free to try this! -Develop a Job class, derived from either the `nautobot_ssot.jobs.base.DataSource` or `nautobot_ssot.jobs.base.DataTarget` classes provided by this Nautobot app, and implement the methods to populate the `self.source_adapter` and `self.target_adapter` attributes that are used by the built-in implementation of `sync_data`. This `sync_data` method is an opinionated way of running the process including some performance data (more about this in the next section), but you could overwrite it completely or any of the key hooks that it calls. +## Putting it Together in a Job -The methods [`calculate_diff`][nautobot_ssot.jobs.base.DataSyncBaseJob.calculate_diff] and [`execute_sync`][nautobot_ssot.jobs.base.DataSyncBaseJob.execute_sync] are both implemented by default, using the data that is loaded into the adapters through the respective methods. Note that `execute_sync` will _only_ execute when dry-run is set to false. +Having built the model and both adapters, we can now put everything together into a job to finish up the integration. -Optionally, on your Job class, also implement the [`lookup_object`][nautobot_ssot.jobs.base.DataSyncBaseJob.lookup_object], [`data_mapping`][nautobot_ssot.jobs.base.DataSyncBaseJob.data_mappings], and/or [`config_information`][nautobot_ssot.jobs.base.DataSyncBaseJob.config_information] APIs (to provide more information to the end user about the details of this Job), as well as the various metadata properties on your Job's Meta inner class. Refer to the example Jobs provided in this Nautobot app for examples and further details. -Install your Job via any of the supported Nautobot methods (installation into the `JOBS_ROOT` directory, inclusion in a Git repository, or packaging as part of an app) and it should automatically become available! +```python +# tutorial_ssot_app/jobs.py +from nautobot_ssot.jobs.base import DataSource +from nautobot.apps.jobs import Job, register_jobs +from tutorial_ssot_app.ssot.adapters import MySSoTRemoteAdapter, MySSoTNautobotAdapter -### Extra Step: Implementing `create`, `update` and `delete` +class TutorialDataSource(DataSource, Job): + class Meta: + name = "Example Data Source" -If you are synchronizing data _to_ Nautobot and not _from_ Nautobot, you can entirely skip this step. The `nautobot_ssot.contrib.NautobotModel` class provides this functionality automatically. + def load_source_adapter(self): + self.source_adapter = MySSoTRemoteAdapter() + self.source_adapter.load() -If you need to perform the `create`, `update` and `delete` operations on the remote system however, it will be up to you to implement those on your model. + def load_target_adapter(self): + self.target_adapter = MySSoTNautobotAdapter() + self.target_adapter.load() -!!! note - You still want your models to adhere to the [modeling guide](../user/modeling.md), since it provides you the auto-generated `load` function for the diffsync adapter on the Nautobot side. +register_jobs(TutorialDataSource) +``` -!!! warning - Special care should be taken when synchronizing new Devices with children Interfaces into a Nautobot instance that also defines Device Types with Interface components of the same name. When the new Device is created in Nautobot, its Interfaces will also be created as defined in the respective Device Type. As a result, when SSoT will attempt to create the children Interfaces loaded by the remote adapter, these will already exist in the target Nautobot system. In this scenario, if not properly handled, the sync will fail! Possible remediation steps may vary depending on the specific use-case, therefore this is left as an exercise to the reader/developer to solve for their specific context. \ No newline at end of file +At this point, the job will show up in the web GUI, and you can enable it and even run it! You should see the objects you specified in the remote adapter being synchronized into Nautobot now. Also take a look at the "SSoT Sync Details" button in the top right of the job result page to get some more information on what diffsync is doing. diff --git a/docs/user/modeling.md b/docs/dev/modeling.md similarity index 100% rename from docs/user/modeling.md rename to docs/dev/modeling.md diff --git a/docs/user/app_getting_started.md b/docs/user/app_getting_started.md index 741069976..3f5fac67f 100644 --- a/docs/user/app_getting_started.md +++ b/docs/user/app_getting_started.md @@ -44,3 +44,5 @@ PLUGINS_CONFIG = { ## What are the next steps? You can check out the [Use Cases](app_use_cases.md) section for more examples. + +Alternatively, if you intend to build your own integration, check out [Developing Data Source and Data Target Jobs](../dev/jobs.md). diff --git a/mkdocs.yml b/mkdocs.yml index ded5b3db3..230b0631d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -113,7 +113,6 @@ nav: - Infoblox: "user/integrations/infoblox.md" - IPFabric: "user/integrations/ipfabric.md" - ServiceNow: "user/integrations/servicenow.md" - - Modeling: "user/modeling.md" - Performance: "user/performance.md" - Frequently Asked Questions: "user/faq.md" - External Interactions: "user/external_interactions.md" @@ -147,11 +146,13 @@ nav: - v1.1: "admin/release_notes/version_1.1.md" - v1.0: "admin/release_notes/version_1.0.md" - Developer Guide: + - "Tutorial: Developing Jobs": "dev/jobs.md" + - "How To: Debugging Jobs": "dev/debugging.md" + - "Reference: Issues": "dev/issues.md" + - "Reference: Modeling": "dev/modeling.md" + - Development Environment: "dev/dev_environment.md" - Extending the App: "dev/extending.md" - - Developing Jobs: "dev/jobs.md" - - Debugging Jobs: "dev/debugging.md" - Contributing to the App: "dev/contributing.md" - - Development Environment: "dev/dev_environment.md" - Code Reference: - "dev/code_reference/index.md" - Package: "dev/code_reference/package.md" diff --git a/poetry.lock b/poetry.lock index e0d827150..3e1ca7390 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "aiodns" @@ -2093,6 +2093,17 @@ files = [ {file = "ijson-3.2.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4a3a6a2fbbe7550ffe52d151cf76065e6b89cfb3e9d0463e49a7e322a25d0426"}, {file = "ijson-3.2.3-cp311-cp311-win32.whl", hash = "sha256:6a4db2f7fb9acfb855c9ae1aae602e4648dd1f88804a0d5cfb78c3639bcf156c"}, {file = "ijson-3.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:ccd6be56335cbb845f3d3021b1766299c056c70c4c9165fb2fbe2d62258bae3f"}, + {file = "ijson-3.2.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:055b71bbc37af5c3c5861afe789e15211d2d3d06ac51ee5a647adf4def19c0ea"}, + {file = "ijson-3.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c075a547de32f265a5dd139ab2035900fef6653951628862e5cdce0d101af557"}, + {file = "ijson-3.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:457f8a5fc559478ac6b06b6d37ebacb4811f8c5156e997f0d87d708b0d8ab2ae"}, + {file = "ijson-3.2.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9788f0c915351f41f0e69ec2618b81ebfcf9f13d9d67c6d404c7f5afda3e4afb"}, + {file = "ijson-3.2.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fa234ab7a6a33ed51494d9d2197fb96296f9217ecae57f5551a55589091e7853"}, + {file = "ijson-3.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdd0dc5da4f9dc6d12ab6e8e0c57d8b41d3c8f9ceed31a99dae7b2baf9ea769a"}, + {file = "ijson-3.2.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c6beb80df19713e39e68dc5c337b5c76d36ccf69c30b79034634e5e4c14d6904"}, + {file = "ijson-3.2.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a2973ce57afb142d96f35a14e9cfec08308ef178a2c76b8b5e1e98f3960438bf"}, + {file = "ijson-3.2.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:105c314fd624e81ed20f925271ec506523b8dd236589ab6c0208b8707d652a0e"}, + {file = "ijson-3.2.3-cp312-cp312-win32.whl", hash = "sha256:ac44781de5e901ce8339352bb5594fcb3b94ced315a34dbe840b4cff3450e23b"}, + {file = "ijson-3.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:0567e8c833825b119e74e10a7c29761dc65fcd155f5d4cb10f9d3b8916ef9912"}, {file = "ijson-3.2.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:eeb286639649fb6bed37997a5e30eefcacddac79476d24128348ec890b2a0ccb"}, {file = "ijson-3.2.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:396338a655fb9af4ac59dd09c189885b51fa0eefc84d35408662031023c110d1"}, {file = "ijson-3.2.3-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e0243d166d11a2a47c17c7e885debf3b19ed136be2af1f5d1c34212850236ac"}, @@ -2367,13 +2378,13 @@ testing = ["Django (<3.1)", "colorama", "docopt", "pytest (>=3.9.0,<5.0.0)"] [[package]] name = "jinja2" -version = "3.1.3" +version = "3.1.4" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" files = [ - {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, - {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, ] [package.dependencies] @@ -3144,13 +3155,13 @@ testing = ["beautifulsoup4", "coverage", "docutils (>=0.17.0,<0.18.0)", "pytest [[package]] name = "nautobot" -version = "2.2.2" +version = "2.2.5" description = "Source of truth and network automation platform." optional = false python-versions = "<3.12,>=3.8" files = [ - {file = "nautobot-2.2.2-py3-none-any.whl", hash = "sha256:4a77b13d60f004815007d519f29602bba5b9ff899d235bb055a64ce9b6f461ce"}, - {file = "nautobot-2.2.2.tar.gz", hash = "sha256:502fd0bf8691900b1c86c307e8bb3765990890a02e656c4af8e0b9cc3b7cc6f4"}, + {file = "nautobot-2.2.5-py3-none-any.whl", hash = "sha256:8b4256cb5f76b13d56c754b8a04e2869bc78d6a6593b2e7aae8094073320cb49"}, + {file = "nautobot-2.2.5.tar.gz", hash = "sha256:0b0ac6aae922092dad271feccfef3efe1e1482284b23d0acbdb0c61f78227b57"}, ] [package.dependencies] @@ -3181,7 +3192,7 @@ emoji = ">=2.11.0,<2.12.0" GitPython = ">=3.1.43,<3.2.0" graphene-django = ">=2.16.0,<2.17.0" graphene-django-optimizer = ">=0.8.0,<0.9.0" -Jinja2 = ">=3.1.3,<3.2.0" +Jinja2 = ">=3.1.4,<3.2.0" jsonschema = ">=4.7.0,<5.0.0" Markdown = ">=3.5.2,<3.6.0" MarkupSafe = ">=2.1.5,<2.2.0" @@ -3195,16 +3206,16 @@ psycopg2-binary = ">=2.9.9,<2.10.0" python-slugify = ">=8.0.3,<8.1.0" pyuwsgi = ">=2.0.23,<2.1.0" PyYAML = ">=6.0,<6.1" -social-auth-app-django = ">=5.4.0,<5.5.0" +social-auth-app-django = ">=5.4.1,<5.5.0" svgwrite = ">=1.4.2,<1.5.0" [package.extras] -all = ["django-auth-ldap (>=4.7.0,<4.8.0)", "django-storages (>=1.14.2,<1.15.0)", "mysqlclient (>=2.2.3,<2.3.0)", "napalm (>=4.1.0,<4.2.0)", "social-auth-core[openidconnect,saml] (>=4.5.3,<4.6.0)"] +all = ["django-auth-ldap (>=4.7.0,<4.8.0)", "django-storages (>=1.14.2,<1.15.0)", "mysqlclient (>=2.2.3,<2.3.0)", "napalm (>=4.1.0,<4.2.0)", "social-auth-core[saml] (>=4.5.3,<4.6.0)"] ldap = ["django-auth-ldap (>=4.7.0,<4.8.0)"] mysql = ["mysqlclient (>=2.2.3,<2.3.0)"] napalm = ["napalm (>=4.1.0,<4.2.0)"] remote-storage = ["django-storages (>=1.14.2,<1.15.0)"] -sso = ["social-auth-core[openidconnect,saml] (>=4.5.3,<4.6.0)"] +sso = ["social-auth-core[saml] (>=4.5.3,<4.6.0)"] [[package]] name = "nautobot-capacity-metrics" @@ -3696,6 +3707,7 @@ files = [ {file = "psycopg2_binary-2.9.9-cp311-cp311-win32.whl", hash = "sha256:dc4926288b2a3e9fd7b50dc6a1909a13bbdadfc67d93f3374d984e56f885579d"}, {file = "psycopg2_binary-2.9.9-cp311-cp311-win_amd64.whl", hash = "sha256:b76bedd166805480ab069612119ea636f5ab8f8771e640ae103e05a4aae3e417"}, {file = "psycopg2_binary-2.9.9-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8532fd6e6e2dc57bcb3bc90b079c60de896d2128c5d9d6f24a63875a95a088cf"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0605eaed3eb239e87df0d5e3c6489daae3f7388d455d0c0b4df899519c6a38d"}, {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f8544b092a29a6ddd72f3556a9fcf249ec412e10ad28be6a0c0d948924f2212"}, {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d423c8d8a3c82d08fe8af900ad5b613ce3632a1249fd6a223941d0735fce493"}, {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e5afae772c00980525f6d6ecf7cbca55676296b580c0e6abb407f15f3706996"}, @@ -3704,6 +3716,8 @@ files = [ {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:cb16c65dcb648d0a43a2521f2f0a2300f40639f6f8c1ecbc662141e4e3e1ee07"}, {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:911dda9c487075abd54e644ccdf5e5c16773470a6a5d3826fda76699410066fb"}, {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:57fede879f08d23c85140a360c6a77709113efd1c993923c59fde17aa27599fe"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-win32.whl", hash = "sha256:64cf30263844fa208851ebb13b0732ce674d8ec6a0c86a4e160495d299ba3c93"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-win_amd64.whl", hash = "sha256:81ff62668af011f9a48787564ab7eded4e9fb17a4a6a74af5ffa6a457400d2ab"}, {file = "psycopg2_binary-2.9.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2293b001e319ab0d869d660a704942c9e2cce19745262a8aba2115ef41a0a42a"}, {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03ef7df18daf2c4c07e2695e8cfd5ee7f748a1d54d802330985a78d2a5a6dca9"}, {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a602ea5aff39bb9fac6308e9c9d82b9a35c2bf288e184a816002c9fae930b77"}, @@ -4292,6 +4306,7 @@ files = [ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, @@ -4299,8 +4314,16 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, @@ -4317,6 +4340,7 @@ files = [ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, @@ -4324,6 +4348,7 @@ files = [ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, @@ -4716,13 +4741,13 @@ files = [ [[package]] name = "social-auth-app-django" -version = "5.4.0" +version = "5.4.1" description = "Python Social Authentication, Django integration." optional = false python-versions = ">=3.8" files = [ - {file = "social-auth-app-django-5.4.0.tar.gz", hash = "sha256:09ac02a063cb313eed5e9ef2f9ac4477c8bf5bbd685925ff3aba43f9072f1bbb"}, - {file = "social_auth_app_django-5.4.0-py3-none-any.whl", hash = "sha256:28c65b2e2092f30cdb3cf912eeaa6988b49fdf4001b29bd89e683673d700a38e"}, + {file = "social-auth-app-django-5.4.1.tar.gz", hash = "sha256:2a43cde559dd34fdc7132417b6c52c780fa99ec2332dee9f405b4763f371c367"}, + {file = "social_auth_app_django-5.4.1-py3-none-any.whl", hash = "sha256:7519f186c63c50f2d364457b236f051338d194bcface55e318a6a705c5213477"}, ] [package.dependencies] diff --git a/tasks.py b/tasks.py index de02c8a7a..1552a57bb 100644 --- a/tasks.py +++ b/tasks.py @@ -48,7 +48,7 @@ def is_truthy(arg): namespace.configure( { "nautobot_ssot": { - "nautobot_ver": "2.1.0", + "nautobot_ver": "2.2.5", "project_name": "nautobot-ssot", "python_ver": "3.11", "local": False,