Repository: nautobot/nautobot

Issue:
Add support for an OS Model to define target OS image per device
### Proposed Functionality
Support a model to define OSs that should be used by each device and ensure there is also a relationship between vendor and platform.

### Use Case
As a user, I should be able to define the intended OS or list of supported OSs per device. I should preferably be able to run search queries within the UI using greater than and less than (<>) operators to verify if my devices.

### Database Changes
Yes, will require a new database model(s), as well as updates to Vendor and Platform models

### External Dependencies
Maybe - need to explore external library(ies) for parsing idiosyncratic vendor OS version strings.



Discussion:
Other version controlled artifacts should be taken into consideration in this model?
Like firmware for specific cards, bootrom or even config templates. All are subject to tracking of desired version of the artifact for a device/site/tenant etc. We use the Cfg Context hierarchy for this atm, but this doesn't allow for nice presentation or reporting/searching.
@sdktr can you share an example of your current YAML data used in a config context for this?


I added an example to [the nautobot demo instance](https://demo.nautobot.com/dcim/devices/5989cc34-5b2f-4b92-8f90-c6cd2f425144/config-context/)

### Artifact tracking (firmware/bootrom etc)
One of the artifacts we track a desired version of is declared as 'device_fw_image'. It's logic is based upon a config context key lookup thats concatenated of: 
`device_fw_image_{{ manufacturer }}_{{ devicetype['slug'] }}_desired`
Eg
`device_fw_image_cisco_c11174p_desired:`
The [Platform Config Context](https://demo.nautobot.com/extras/config-contexts/ff04f0ef-f9e5-41d2-9ecd-5a754370f93d/) contains all the default versions per supported Device Type. It has the lowest weight so can be overriden by a [Tenant specific Config Context](https://demo.nautobot.com/extras/config-contexts/c4cc6b0a-57a5-4d94-97bc-8812ece1b31e/) for example.

Our Inventory plugin contains logic that compares the value of the _desired firmware image to a Custom Field with the name device_fw_image_active. If diverse the device is added to a group 'firmware_mismatch' and can be directly targetted by a playbook.

Outside of the SoT there is a seperate YAML file containing a datamodel for 'allowed migration paths'. Down to the combination of `evice type` + `active firmware` we want to whitelist the allowed upgrade paths to the `desired` artifact. We only add an entry to that `allow list` if we've verified an upgrade path first. A mismatch in a upgrade path lookup can result in an intermediate version hint that we pick as an alternative upgrade. Maybe the next attempt will result in a succesfull upgrade based on this intermediate.

### Config Template tracking
Desired config templates are looked up with mostly the same logic. When no device-type specific key is found though, we try to lookup a key with the syntax: `device_config_template_{{ manufacturer }}_{{ platform }}_desired`.  We want to add the `device role` key to this as well since configs tend to focus on a device role as we found out.
The value of this key of course is subject to config context hierarchy. A default template that should get you started on a platform+role combo on global-ish cfg context and overruled if needed on tenant/site/device level.
The deployed config template (_active) is also stored in a Custom Field and subject to the same 'template revision needed' checks as the artifact mgmt.

#### PS on 'current' vs 'desired'
The storage of the actually deployed version should be stored 'somewhere else' since it's not part of the _desired_ but _current_ branch of the network automation. Ideally it should be some sort of factcache that containts timeseries based copy of actual device state data that can be easily compared to values of the SoT. We mis-use custom fields until we come up with a good solution for this.



@sdktr thanks for your detailed message.
please keep in mind that the sandbox will be reset every night so the links won't work tomorrow, or they will work but your changes won't be there anymore
> @sdktr thanks for your detailed message.
> 
> please keep in mind that the sandbox will be reset every night so the links won't work tomorrow, or they will work but your changes won't be there anymore

Ah! Too bad I learned that the hard way. The story still checks out for you, doesn't it?
Just adding some additional use cases for consideration for this feature..

### Use Cases:

As Austin the NAE, I want to write a Job that alerts the network team what devices are running an OS version that goes EoL in 12-18 months.

As Nelly the Network Engineer, I want to be able to see what OS versions are approved for my network devices and I want to understand what licensing requirements a particular OS version has.

As Ozzie the Operator, I want to know that the network devices in my inventory have the capacity to store various OS software images.

As Tammy the Tools Integrator, I want to ensure that a particular image is a stable release and what the file type is to ensure my scripts can automate network upgrades.
@pke11y I would update your  Use Cases to use the personas defined in the Wiki. https://github.com/nautobot/nautobot/wiki/Personas
@smk4664 thanks for that. Wasn't aware we had to follow the specific persona. Updated comment as requested.
Excited for this :)
We've got a plugin that is in development that has models for valid software images per device/device type as well as a recommended image. I think that may cover many of these items. It also will have hardware life cycle and maintenance contracts for devices, device types, and inventory items. 

I believe that it may make sense to evaluate the plugin, and then see if it makes sense to move into core?
Something else that may be useful (to me, for sure) for this is to be able to define the image checksum(s) for any of the images. I manage a network that has satellite links and its not uncommon for an image transfer to have issues. Currently, I am using checksums (defined in a yml file) to ensure that all the bits are there before a playbook will execute the install/reboot/upgrade process.
@jvanderaa with discussion with the team, we think it makes sense for you to stay current course with the plugin you mentioned.  When that gets released, we'll see what the feedback is and make a determination after feedback, to see if it makes sense to bring any part of that into core.  So, you may want to think about what you have in that should it be 1 or more plugins, etc.  

Will keep this issue open to track the release of the plugin for now and for visibility, but for clarity, right now this wouldn't be in core in th 1.3 milestone.
@jvanderaa  Any timeline on the plugin?
@edurguti we should have an update on this next week about when we can expect for it to become available. 
@jvanderaa can you tell us if you have a timeline estimate? We are super excited...
@edurguti @vladanmilosevic73 I am trying to get this through our our Open Source Review Board!
The device lifecycle management plugin has been released > https://github.com/nautobot/nautobot-plugin-device-lifecycle-mgmt
We need to spike on what a migration pattern for a plugin into a core feature set so that we can start the process of integrating plugins like Device Lifecycle Management into the core in the future.
We will insource the DLM software models into core. DLM will retain all other functionality and will provide migrations to use the models now provided in core.

The core team will assist by writing the migrations within the DLM app.

We will insource the `SoftwareLCM` (naming it `SoftwareVersion`) and the `SoftwareImageLCM` (naming it `SoftwareImage`).

We will also introduce a new `SoftwareFamily` model akin to `HardwareFamily`. This _may_ take the place of the `device_platform` FK on `SoftwareVersion` in the future.
This story will track only porting of `SoftwareVersion` and `SoftwareImage`. Additional stories will be created for the other work.

Resolving patch:
diff --git a/nautobot/dcim/api/urls.py b/nautobot/dcim/api/urls.py
--- a/nautobot/dcim/api/urls.py
+++ b/nautobot/dcim/api/urls.py
@@ -73,7 +73,7 @@
 # Software images
 router.register("software-images", views.SoftwareImageViewSet)
 router.register("software-versions", views.SoftwareVersionViewSet)
-router.register("device-type-to-software-image", views.DeviceTypeToSoftwareImageViewSet)
+router.register("device-types-to-software-images", views.DeviceTypeToSoftwareImageViewSet)
 
 # Miscellaneous
 router.register("connected-device", views.ConnectedDeviceViewSet, basename="connected-device")


Test patch: