mirror of
https://github.com/netbox-community/netbox.git
synced 2026-01-11 21:10:29 +01:00
Storage of contextual configuration data #1107
Closed
opened 2025-12-29 16:28:55 +01:00 by adam
·
38 comments
No Branch/Tag Specified
main
update-changelog-comments-docs
feature-removal-issue-type
20911-dropdown
20239-plugin-menu-classes-mutable-state
21097-graphql-id-lookups
feature
fix_module_substitution
20923-dcim-templates
20044-elevation-stuck-lightmode
feature-ip-prefix-link
v4.5-beta1-release
20068-import-moduletype-attrs
20766-fix-german-translation-code-literals
20378-del-script
7604-filter-modifiers-v3
circuit-swap
12318-case-insensitive-uniqueness
20637-improve-device-q-filter
20660-script-load
19724-graphql
20614-update-ruff
14884-script
02496-max-page
19720-macaddress-interface-generic-relation
19408-circuit-terminations-export-templates
20203-openapi-check
fix-19669-api-image-download
7604-filter-modifiers
19275-fixes-interface-bulk-edit
fix-17794-get_field_value_return_list
11507-show-aggregate-and-rir-on-api
9583-add_column_specific_search_field_to_tables
v4.5.0
v4.4.10
v4.4.9
v4.5.0-beta1
v4.4.8
v4.4.7
v4.4.6
v4.4.5
v4.4.4
v4.4.3
v4.4.2
v4.4.1
v4.4.0
v4.3.7
v4.4.0-beta1
v4.3.6
v4.3.5
v4.3.4
v4.3.3
v4.3.2
v4.3.1
v4.3.0
v4.2.9
v4.3.0-beta2
v4.2.8
v4.3.0-beta1
v4.2.7
v4.2.6
v4.2.5
v4.2.4
v4.2.3
v4.2.2
v4.2.1
v4.2.0
v4.1.11
v4.1.10
v4.1.9
v4.1.8
v4.2-beta1
v4.1.7
v4.1.6
v4.1.5
v4.1.4
v4.1.3
v4.1.2
v4.1.1
v4.1.0
v4.0.11
v4.0.10
v4.0.9
v4.1-beta1
v4.0.8
v4.0.7
v4.0.6
v4.0.5
v4.0.3
v4.0.2
v4.0.1
v4.0.0
v3.7.8
v3.7.7
v4.0-beta2
v3.7.6
v3.7.5
v4.0-beta1
v3.7.4
v3.7.3
v3.7.2
v3.7.1
v3.7.0
v3.6.9
v3.6.8
v3.6.7
v3.7-beta1
v3.6.6
v3.6.5
v3.6.4
v3.6.3
v3.6.2
v3.6.1
v3.6.0
v3.5.9
v3.6-beta2
v3.5.8
v3.6-beta1
v3.5.7
v3.5.6
v3.5.5
v3.5.4
v3.5.3
v3.5.2
v3.5.1
v3.5.0
v3.4.10
v3.4.9
v3.5-beta2
v3.4.8
v3.5-beta1
v3.4.7
v3.4.6
v3.4.5
v3.4.4
v3.4.3
v3.4.2
v3.4.1
v3.4.0
v3.3.10
v3.3.9
v3.4-beta1
v3.3.8
v3.3.7
v3.3.6
v3.3.5
v3.3.4
v3.3.3
v3.3.2
v3.3.1
v3.3.0
v3.2.9
v3.2.8
v3.3-beta2
v3.2.7
v3.3-beta1
v3.2.6
v3.2.5
v3.2.4
v3.2.3
v3.2.2
v3.2.1
v3.2.0
v3.1.11
v3.1.10
v3.2-beta2
v3.1.9
v3.2-beta1
v3.1.8
v3.1.7
v3.1.6
v3.1.5
v3.1.4
v3.1.3
v3.1.2
v3.1.1
v3.1.0
v3.0.12
v3.0.11
v3.0.10
v3.1-beta1
v3.0.9
v3.0.8
v3.0.7
v3.0.6
v3.0.5
v3.0.4
v3.0.3
v3.0.2
v3.0.1
v3.0.0
v2.11.12
v3.0-beta2
v2.11.11
v2.11.10
v3.0-beta1
v2.11.9
v2.11.8
v2.11.7
v2.11.6
v2.11.5
v2.11.4
v2.11.3
v2.11.2
v2.11.1
v2.11.0
v2.10.10
v2.10.9
v2.11-beta1
v2.10.8
v2.10.7
v2.10.6
v2.10.5
v2.10.4
v2.10.3
v2.10.2
v2.10.1
v2.10.0
v2.9.11
v2.10-beta2
v2.9.10
v2.10-beta1
v2.9.9
v2.9.8
v2.9.7
v2.9.6
v2.9.5
v2.9.4
v2.9.3
v2.9.2
v2.9.1
v2.9.0
v2.9-beta2
v2.8.9
v2.9-beta1
v2.8.8
v2.8.7
v2.8.6
v2.8.5
v2.8.4
v2.8.3
v2.8.2
v2.8.1
v2.8.0
v2.7.12
v2.7.11
v2.7.10
v2.7.9
v2.7.8
v2.7.7
v2.7.6
v2.7.5
v2.7.4
v2.7.3
v2.7.2
v2.7.1
v2.7.0
v2.6.12
v2.6.11
v2.6.10
v2.6.9
v2.7-beta1
Solcon-2020-01-06
v2.6.8
v2.6.7
v2.6.6
v2.6.5
v2.6.4
v2.6.3
v2.6.2
v2.6.1
v2.6.0
v2.5.13
v2.5.12
v2.6-beta1
v2.5.11
v2.5.10
v2.5.9
v2.5.8
v2.5.7
v2.5.6
v2.5.5
v2.5.4
v2.5.3
v2.5.2
v2.5.1
v2.5.0
v2.4.9
v2.5-beta2
v2.4.8
v2.5-beta1
v2.4.7
v2.4.6
v2.4.5
v2.4.4
v2.4.3
v2.4.2
v2.4.1
v2.4.0
v2.3.7
v2.4-beta1
v2.3.6
v2.3.5
v2.3.4
v2.3.3
v2.3.2
v2.3.1
v2.3.0
v2.2.10
v2.3-beta2
v2.2.9
v2.3-beta1
v2.2.8
v2.2.7
v2.2.6
v2.2.5
v2.2.4
v2.2.3
v2.2.2
v2.2.1
v2.2.0
v2.1.6
v2.2-beta2
v2.1.5
v2.2-beta1
v2.1.4
v2.1.3
v2.1.2
v2.1.1
v2.1.0
v2.0.10
v2.1-beta1
v2.0.9
v2.0.8
v2.0.7
v2.0.6
v2.0.5
v2.0.4
v2.0.3
v2.0.2
v2.0.1
v2.0.0
v2.0-beta3
v1.9.6
v1.9.5
v2.0-beta2
v1.9.4-r1
v1.9.3
v2.0-beta1
v1.9.2
v1.9.1
v1.9.0-r1
v1.8.4
v1.8.3
v1.8.2
v1.8.1
v1.8.0
v1.7.3
v1.7.2-r1
v1.7.1
v1.7.0
v1.6.3
v1.6.2-r1
v1.6.1-r1
1.6.1
v1.6.0
v1.5.2
v1.5.1
v1.5.0
v1.4.2
v1.4.1
v1.4.0
v1.3.2
v1.3.1
v1.3.0
v1.2.2
v1.2.1
v1.2.0
v1.1.0
v1.0.7-r1
v1.0.7
v1.0.6
v1.0.5
v1.0.4
v1.0.3-r1
v1.0.3
1.0.0
Labels
Clear labels
beta
breaking change
complexity: high
complexity: low
complexity: medium
needs milestone
netbox
pending closure
plugin candidate
pull-request
severity: high
severity: low
severity: medium
status: accepted
status: backlog
status: blocked
status: duplicate
status: needs owner
status: needs triage
status: revisions needed
status: under review
topic: GraphQL
topic: Internationalization
topic: OpenAPI
topic: UI/UX
topic: cabling
topic: event rules
topic: htmx navigation
topic: industrialization
topic: migrations
topic: plugins
topic: scripts
topic: templating
topic: testing
type: bug
type: deprecation
type: documentation
type: feature
type: housekeeping
type: translation
Mirrored from GitHub Pull Request
No Label
status: accepted
Milestone
No items
No Milestone
Projects
Clear projects
No project
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: starred/netbox#1107
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @bdlamprecht on GitHub (Jul 14, 2017).
Issue type:
Feature Request
Python version:
2.7.12
NetBox version:
2.0.8
With the goal of NetBox being THE "Source of Truth" for the desired state of a network, I noticed that it does not currently have a location to store values for IPv4/IPv6 addresses of DNS, NTP, TACACS, SYSLOG, SNMP, and other servers which support network devices (as well as servers).
While thinking through real-world scenarios, these values are usually per region and/or per site, so
custom_fieldscould be used, but I think it would be applicable to a large portion of NetBox users to dedicate the time to implement this update to the ORM schema.For my thinking, it I believe it would be best to possibly create a new section in
IPAMto store thesekey:valuepairs which could then be tied to eitherregionsorsitesto minimize querying the DB so often, but it is highly likely that some other place would be more beneficial.Pinging @jeremystretch for his thoughts.
@jeremystretch commented on GitHub (Jul 18, 2017):
I definitely see the value in adding something like this to NetBox. The tricky part is going to be implementing it. We want to provide something more valuable than a vanilla key/value store: as mentioned, we should be able to tie values to NetBox objects like regions, sites, etc. But we also want to ensure we maintain sufficient flexibility, not knowing what people might want to use it for. We also don't want to duplicate the functionality provided by custom fields.
At a higher level, this is going to tie in somewhat with configuration management (e.g. the ability to render device configurations using templates and NetBox data), but I haven't given that topic much thought yet.
@bdlamprecht commented on GitHub (Jul 18, 2017):
I agree that great care needs to be taken to ensure we don't just implement another version of
custom_fields. As you stated, you haven't given it much thought yet, but when you do, I'd be happy to help think through some of the overall structure and how that information should be tied together.I'm not an expert by any means, but would just like to contribute back for all the help that has been provided.
@jeremystretch commented on GitHub (Jul 18, 2017):
@bdlamprecht What prompted you to open this issue? Are you interested in generating device configs using this data as context, or did you have something different in mind?
@bdlamprecht commented on GitHub (Jul 18, 2017):
Yes, I'm working on a POC using NetBox as a "Source of Truth" for Ansible which will then create / manage configuration templates for my large enterprise customer (1000+ templates) and push those changes onto the device using NAPALM (or possibly another Ansible "module").
Our "service delivery" team that installs the so-called "templates" which are created by hand rarely get them correct (which is understandable as they are, in my opinion, unnecessarily complex). This project I'm working on is to try and automate and orchestrate that process.
@jeremystretch commented on GitHub (Jul 18, 2017):
Here's an idea: We create a
ConfigContextmodel under theextrasapp to hold arbitrary key/value data in the user's choice of formats (YAML, JSON, etc.). Each ConfigContext instance can be tied to a set of regions and/or sites, with site context data superseding region context data. This allows users to define arbitrary data without incurring significant overhead.For example, I create a ConfigContext assigned to a region called "North America" with the following:
Then I create a second ConfigContext for a specific site in that region with special DNS servers:
The context available for a device in that site would render as:
We could make this available via the API, but the next stop logical step would be to render device configs from templates directly within NetBox.
@bdlamprecht commented on GitHub (Jul 18, 2017):
Great, I love the idea of the "more-specific" information overrides the other "generic" information.
And the sooner it becomes available in the API the better, but I don't want to sound demanding. 😄
I'm confused by your last statement however...
Are you proposing that NetBox be used to store device configuration templates? If so, that is a huge endeavor, but not sure it is needed.
The POC I'm working on now would store the templates in GIT so you can generate the configs and push them to the devices based off of which "release" you have "checked-out" in GIT. I don't feel the need to duplicate efforts by other tools already in existence.
All this being said, it is great work and, once again, I really appreciate all of the work that you're doing with developing NetBox.
@jeremystretch commented on GitHub (Jul 18, 2017):
Not store them natively, but perhaps read them from a repository.
What are you using to render the templates? Presumably you're pulling data from NetBox via the API?
@bdlamprecht commented on GitHub (Jul 18, 2017):
Well, don't judge me as I'm new to the developer world and I'm fairly sure there's a better / more efficient way to accomplish the end goal I have.
Anyways, I'm using the Ansible
urimodule to query the NetBox API and thenregisterthe results into variables to be used by the Jinja2 templates I've created for both Cisco and Juniper devices.@jeremystretch commented on GitHub (Jul 19, 2017):
Hey, me too! 😄
That seems like a very reasonable approach. So it seems like in your case just making additional context data available via the API would be sufficient.
@bdlamprecht commented on GitHub (Jul 19, 2017):
Yeah, I think that would work.
I'm not sure if I made this clear, but it would be important, at least for my scenario, to have the new
ConfigContextdata available (through the API) on a per-device view.As an example, something like
/api/extras/[NAME_HERE]/?device=[X]which would use the hierarchy you explained above to provide the more-specific information (region -> site -> device). However, if that is not possible due to the structure of the ORM, I'll take anything you can give me. 😄BTW, it doesn't seem like you're new to the developer world based off of how quickly you implement new features and update NetBox, but if so 🥇
@lampwins commented on GitHub (Jul 19, 2017):
So this is really exciting to see. I have been building this sort of thing in other places in my automation suite integrating with netbox. I have been really flustered because IMHO the best place for this is inside of netbox itself. That being said, I do have a few ideas (and would love to help see them through)...
Today I store this data either in group vars in ansible playbooks (for generic role based config) and a hacked together CMDB (for device specific config).
@jeremystretch I think it is best to store this in netbox. I would be fine storing this as actual yaml (as flat files) or as json in postgres. That is flexible though; if the solution is elegant, I don't really care.
I would want to see the ability to assign a
ConfigContextto (but not necessarily limited to):Device type and role are the interesting ones as they would allow me to derive use case for a device (as has been previously discussed in the Network to Code slack).
The hard part with allowing all of those is defining the hierarchy of precedence. To that end, I think the solution is allow it to be configurable rather than hard set. So perhaps have a default hierarchy but allow the user to modify it if the default presents conflicts for their use case (I assume it would, but maybe I am over thinking this part).
@jeremystretch commented on GitHub (Jul 19, 2017):
I was referring to configuration templates. Those won't be managed by NetBox directly.
@jeremystretch commented on GitHub (Jul 21, 2017):
I'm trying to decide how best to implement this in the API.
Option A: Always include config context with the DeviceSerializer (including when listing multiple devices). This seems terribly inefficient.
Option B: Leave DeviceSerializer alone and create a detail route to retrieve the config context for a particular device, e.g.
GET /api/dcim/devices/<pk>/config-context/. This would require two API requests when rendering a config: one for the device attributes and one for the context.Option C: Implement a request parameter like
?include_context=1, which when passed would add theconfig_contextfield to DeviceSerializer. (If omitted, the field would not be present.) I'm not sure if we want to set a precedence for adding/removing fields dynamically. I'd also have to give some more thought to structuring the queryset, since we have to addprefetch_relatedto pull in the relevant ConfigContexts, which should be avoided if we're not going to use it.Open to other suggestions.
@lampwins commented on GitHub (Jul 23, 2017):
I would not do option A. I only want the config context in specific situations, not all.
I would like to see a normal set of endpoints for
ConfigContext, so/api/dcim/config-context/and so forth. Then include serializers for theConfigContextpk on all objects that accept contexts. I like this because it allows me to retrieve only certain contexts, if that is all I need, i.e. just site context.This plays into my earlier comments on dynamic hierarchy. In this model the dev/api user gets full control in how they interpret the hierarchy. Obviously this means if they want to build a full config, they may need a series of api calls all at once (is that really a bad thing?) to get all the contexts for a config. To that end, I also like options B which would implement the "default hierarchy" I proposed.
Just my thoughts.
@bdlamprecht commented on GitHub (Jul 24, 2017):
I agree that Option A is definitely a no-go for me.
However, I'm torn between Option B and Option C. Let me explain...
Not necessarily in my scenario I detailed above, but I can foresee some circumstances where not all
ConfigContextinformation that is defined for either a region, site, or device would be needed for each API call (i.e. NTP servers, DNS servers, SNMP servers, TACACS servers, and any otherkey-valuedefined by the user). That would be an overload.Now that I think about it, perhaps a mixture between both Option B and Option C would be ideal if the API could be structured in a way where you could do something along the lines of
/api/dcim/devices/<pk>/include_context=dns,ntp,snmpwhich would return only the values stored in the keysdnsandntpandsnmpbut nottacacs(orvlanas mentioned in #1367). That would allow the user ultimate control over what they want it to do.As @jeremystretch mentioned, that WOULD set a "precedence for adding/removing fields dynamically". However, I believe that it would add value and it would be up to the end-user to implement it correctly and understand the pros and cons to structure their data correctly. Some documentation on the "whys" would probably be required 😉 and I'd be willing to help write it once I understand the structure that is implemented.
Again, these thoughts are coming from a newbie to the developer world so this might be fairly complex to do or not even possible at all, so take my comments with a "grain-of-salt".
@cimnine commented on GitHub (Jul 25, 2017):
I would vote against this, as it makes the api a lot harder to query. It seems rather simple to me to only use the values you need on the client side. A system such as the proposed would only make sense to me when one would add a lot (like thousands) of such context fields, because then there would be significant overhead in fetching the values from the database and also in transferring that data to the client.
Option C is my favourite anyway. It is simple enough to implement in an API client. It could even be added to the query for many devices, i.e.
/api/dcim/devices?include_context=1. This could be useful when trying to render a file consisting of the information of many devices.@bdlamprecht commented on GitHub (Jul 25, 2017):
While I'm not opposed to Option C, I don't understand the following statement:
In the example I used above you could always still query the API via
/api/dcim/<pk>to just get the basic details already provided, only by adding?include_context=<item>would you get the additional requested data (you could also have a special keywordallfor?include_context=that returns everything).However, if that is too complex to implement on the server-side, I'd be okay having Option C as it follows the core guidelines of NetBox:
Again, another disclaimer, this is coming from a recent transfer from the networking world to the developer world (DevOps) so I apologize in advance for any ignorance that I'm showing 😃 .
@jeremystretch commented on GitHub (Jul 25, 2017):
I think I prefer option B. It might help to better explain the way I see it working. Let's say you've defined three ConfigContext objects:
Global
Site A
Edge Router
You would retrieve these ConfigContexts via an API endpoint at
/api/configs/config-contexts/. The same endpoint would also be used to create, update, and delete ConfigContexts.Now, rendering the config context for a given device is different. If we want to get the rendered context for a device (e.g. an edge router in site A), we'd request
/api/dcim/devices/<pk>/config-context/, which would return:Notice that the DNS values from the global ConfigContext have been replaced by the ConfigContext for site A. It's important to point out that this request does not return a real ConfigContext object, but rather a dictionary compiled from all ConfigContexts applicable to this device.
@bdlamprecht commented on GitHub (Jul 25, 2017):
Yeah, I see where you are coming from. This statement is going to show my inexperience, but in all reality, the chance of having too many
ConfigContextobjects to make it have any noticeable affect on performance is really quite low, so I'd like to retract my previous statements above.The way you explained the rendering above makes sense to me as well. As you mentioned previously, this would allow the current API to continue to function as it has while also allowing an addtional API call to
/config-context/to gather additional data if and when it is needed (i.e. not all the time).So although my vote doesn't mean much since I'm not the one developing it, I'm onboard for Option B.
@lampwins commented on GitHub (Jul 25, 2017):
@jeremystretch I agree with this implementation. It is important to me to always maintain
ConfigContextas its own entity in the API. This allows me to derive my own hierarchy for rendering the config if for whatever reason I do not agree with the hierarchy implemented at/api/dcim/devices/<pk>/config-context/or only need a partial rendering.@cimnine commented on GitHub (Jul 25, 2017):
I don't follow the argument for option B completely when it comes to
/api/dcim/device/<PK>?include_context=1vs/api/dcim/device/<PK>/config-context.We also define e.g. sites in
/api/dcim/sites/, but they're included in/api/dcim/device/<PK>and I don't have to query/api/dcim/device/<PK>/siteto get the device's site information.I understand, that including the config-context by default is not desirable, because it causes overhead to calculate it's values, but that's why Option C suggests the '?include_context=1' flag.
Myself, I'd rather do one call to the API, than two. At times it's hard to correlate two calls. Also, the second call could fail due to some error condition (e.g. network) that I will have to take care of.
Maybe I could share here one use-case we're investigating and where this feature would become very useful to us: Creating our 'static assignments' dhcp configuration file.
We want to replace our dhcp (and bootp) config file generator to fetch all information from Netbox. It would therefore be very convenient to just query
/api/dcim/devices?include_context=1&some=filters, rather than to make two calls or even two calls per device. Besides the obvious performance penalty for having to makenrequests, there'd be also the overhead on the consuming side of the api to correlate the (at least) two calls.For our solution, we'd also leverage the hierarchical values, e.g.:
Global:
Region 1:
Region 2:
Device 2 (overwrite default value):
@cimnine commented on GitHub (Jul 25, 2017):
I think I now get what you would like to express here:
/api/dcim/devices/<pk>/config-contextare aggregated values over multiple levels and therefore deserve their own endpoint, isn't it?But I still think, that from an API-user's perspective, it'd be favourable to get all required information about an entity in one call to the api, rather than two. (Especially, if I would have to request information on more than one entity, as I have made a case for above.)
@jeremystretch commented on GitHub (Jul 25, 2017):
Yep, exactly.
I agree that it would be ideal, but consider that multiple requests are needed anyway to retrieve interfaces, IP addresses, VLANs, etc. I'm not sure whether it would be practical (or efficient) to try and return all of that in a single response.
@cimnine commented on GitHub (Jul 25, 2017):
That would only be true for that exact case I made above. One might only need the information belonging to a device, e.g. to populate a cloud-init user-data template.
Another argument I'd like to make in favour of
?include_context=1is that this can be added to any api level, e.g. to/api/dcim/devices/<pk>and to/api/dcim/devices.Whereas
/api/dcim/devices/config-contextcould not work, because a device's PK is expected instead ofconfig-context.@jeremystretch commented on GitHub (Jul 25, 2017):
The queryset gets pretty unwieldy when you start pulling devices in bulk. We'd need to prefetch related ConfigContexts for all devices in the list. Since a ConfigContext can be related by region, site, device_role, tenant, and/or tenant_group (plus global contexts), this becomes very inefficient. Additionally, needing to render contexts for multiple devices in a single request seems like an uncommon use case.
@cimnine commented on GitHub (Jul 27, 2017):
Sure, I agree. That's why it's never enabled by default.
It's all the same inefficient when I would query each device one-by-one.
Nevertheless, I was just saying that I would prefer the
?include_context=1variant and I think it is more 'future-proof'. Don't let that stop you from choosing the other option though.@sdktr commented on GitHub (Aug 16, 2017):
Does anyone see it as a nice-to-have to be able to concatenate the content of different inheritence levels instead of getting the most specific value (to whatever preference agreed upon)?
For example:
Global:
Site1:
When querying the device could render:
Since there doesn't seem to be a standard way of doing this with YAML, a syntax would have to be come up with including the 'fixup' code to flatten the aggregated content?
Example taken from
[1]
https://stackoverflow.com/questions/9254178/is-there-yaml-syntax-for-sharing-part-of-a-list-or-map
@jeremystretch commented on GitHub (Aug 16, 2017):
@sdktr The Stack Overflow post you linked mentions the YAML merge key. Seems reasonable, but I'd have to see how involved supporting its logic would be.
@puck commented on GitHub (Aug 21, 2017):
Interesting feature. I can see this being very useful.
With regard to allowing people to store the data how they want in https://github.com/digitalocean/netbox/issues/1349#issuecomment-316194935 if you used the PosgreSQL JSON field type, then you get free form JSON, and could convert to YAML (or whatever) based on what is requested from the API. This would also make it much easier to display, edit & validate this content in the UI in a nice manner, rather than just a giant text field.
PostgreSQL also has nice features now for querying for data inside the JSONB data type fields, so you'd be able to support queries for things in this data in the future (or for people using SQL as their API).
More info on how PG handles JSON is here: https://www.postgresql.org/docs/9.6/static/datatype-json.html
@bdlamprecht commented on GitHub (Sep 7, 2017):
Any updates on this request?
I seem to recall that there was a alpha version working in one of your branches.
Just looking to see if I could get a timeline for when this could be implemented.
@tobymccann commented on GitHub (Sep 9, 2017):
I think this would be an extremely valuable feature that would further Netbox as the SoT. As more organizations automate their environments, data becomes essential and defining a data model for managing device configurations is required.
In our case, we are wanting to manage thousands of devices with Ansible and Ansible Tower/AWX. We have chosen to align with OpenConfig to build our own data model. We would like to store and associate the data in a hierarchy so we can align it with Ansible's group and host variable structure and minimize data duplication (DRY).
Option B that Jeremy mentions would work well for this use case. It would be very interesting to see if YANG or JSON data models (like OpenConfig) could be broken apart and represented within Netbox directly to accomplish the task of loading your organizations data. It would be nice to see a default hierarchy of precedence that could be customized based on what the customer requires:
Tenant
Region
Site
Room
RackGroup
Rack
Pod or Cluster
DeviceType
DeviceRole
Device
Making this data accessible via the API would we critical. With it, customers can create their own dynamic inventories and complete device data models to use with their config management tool of choice, e.g. Ansible, Salt, etc.
Configuration templates should not live in Netbox as these would be managed by the customer in git repositories. Another feature possible with Netbox however would be a front end for managing these templates and integrating with git to do so.
@bdlamprecht commented on GitHub (Sep 27, 2017):
@jeremystretch I know you are very busy, but is there anything I can do to help expedite this request?
As I stated previously, I don't want to be "annoying annoying or in any way demanding", however, the "proof-of-concept" that I've been working on for a few months is highly dependent on this functionality.
To that statement, you mentioned you wanted to "evaluate some other approaches before a commit to the solution" is released.
What do you think would be of the most help to you?
Really interested in getting this working within the next few months.
Again, thanks for your work on this excellent project and especially for releasing it as open-source.
@jeremystretch commented on GitHub (Jan 31, 2018):
I've been thinking about this more lately. I'm hung up on the hierarchical approach because it doesn't allow the assignment of values along two dimensions.
Simple Hierarchy
For example, say we implemented a hierarchy like this, wherein data defined at each layer supercedes any data with the same key defined at the layer above it:
Contextual data could be defined directly on each object as a JSONB field.
But what happens if you need to assign, say,
radius_serverbased on both site and role? Four different sites and three different roles would result in 12 unique values forradius_server(A through L).We can define a unique value for each role and site, but one must take precedence over the other. In this example, role would win because it is below site in the hierarchy.
We can work around this by defining the value for the most common of the three roles at the site level, and then defining values for each of the other two roles directly on the individual devices, but this is redundant and error-prone. And it becomes even less efficient if another dimension like platform or device type is introduced.
Alternative Approach
Instead of defining context data directly on each object as a field, we can introduce a ConfigContext model which has ManyToManyFields typing it to sites, roles, etc., as well as a JSONB field to store the data. In our example above with four sites and three roles, we would create 12 ConfigContexts:
To retrieve all ConfigContexts matching a particular device, we would query like this:
This will return all ConfigContexts assigned to the device's site and/or role (as well as any which are not assigned to any particular site or role). (Of course, we can match on other attributes as well, not just site and role.) This approach should be fine for individual devices, but doesn't scale well when retrieving multiple devices. This is especially true if we decide to filter across multiple tables, like
region=device.site.region.One interesting but somewhat scary approach would be to render the config context for every device each time a ConfigContext is modified, and store the rendered context locally on each device. I'm not sure yet how involved this process would be, but it's worth considering that context is likely to change far less often that it will be read. And pre-computing the rendered context for each device would do wonders for performance, particularly in retrieving many devices via the API.
I'm curious to hear any thoughts others have before I go any further with this.
@jeremystretch commented on GitHub (Jan 31, 2018):
Following up on my comment above after some discussion on NetworkToCode. I envision the ConfigContext model looking something like this:
We may decide to add other fields like
region; this is just an example for the sake of discussion.Each ConfigContext is assigned its relevant sites, roles, etc. The
datafield stores configuration context in JSON format. A weight can be set to prefer one ConfigContext or the other where overlapping data exists (with creation time serving as a tie breaker).Additionally, we'd add a
rendered_contextJSON field on the Device model:Adding, modifying, or deleting a ConfigContext triggers rendering of configuration data for every device. (If we adopt a message queue as has been suggested for #81, this operation can be handled in the background.) This would involve:
1. Retrieve all unique sets of devices
Every device which has the same site, role, tenant, and platform (in this example) will receive the same rendered configuration data. Thus, we only need to render the context for each unique set of devices, rather than for each device independently, using the following query:
2. Render the context data for each set
For each set of devices, we query all relevant ConfigContexts, ordering them by weight and time of creation. A dictionary is created and updated with the contents of each ConfigContext in sequence.
3. Update all devices in the set at once
Using
bulk_update()allows us to hit every device in a set with one query, while avoiding runningsave()on each object (and updating its modification time).Obviously, lots of testing is needed, but it seems plausible. We do potentially end up with quite a bit of redundant data, but this denormalization buys us the ability to return all config context for a device directly from the device table. This means we can pull down the config context for many devices at once with a negligible performance penalty.
@sdktr commented on GitHub (Feb 18, 2018):
Just a thought: should this 'configcontext' and 'custom fields' functionality be merged?
So instead of having a custom field per model, we could use the model as proposed above, but instead of having one context (the configcontext) we could manage them as custom fields with config being just one of them.
To mimic the 'custom fields' functionality as it is today it would just be a one-to-one mapping to the relevant object that stores it (like the simple configcontext proposed before jeremy pitched the configcontext as a seperate model)
An additional benefit of upgrading the 'custom field per table' to the 'context is a seperate model' is that migrations of the database are simplified. There would be just a seperate table/model with all the custom stuff in there instead of each installation having their own tables with custom columns.
Quoting @jeremystretch model for this:
@jeremystretch commented on GitHub (Feb 20, 2018):
Each serves a different purpose and complement one another. A custom field allows a user to specify (even require) a particular value per object. They can be assigned to most primary objects in NetBox. A custom field works well if you need to track values which are generally unique to each object.
A config context is intended to provide additional, optional, free-form data to a set of devices. This would better accommodate data that is the same for each device that meets an arbitrary classification (e.g. all access switches at site D). Also note that contextual data by its nature cannot be declared as mandatory.
@sdktr commented on GitHub (Feb 21, 2018):
I understand that both might have their own primary usage, especially the way they are positioned today.
The reason I came up with this is because of how you proposed to make 'configcontext' it's own seperate model. If I understood your intentions correctly, you did this to have a more flexible combination of data end up at the device level (instead of just inheritance in whatever order).
The way you set up your model is that it could have many-to-many relations to one or more objects. That same datamodel seems to provide a good (imho: better) way of storing the simpler 'custom fields' that have a one-to-one relation to an object today.
The 'data contract' for these 'custom fields 2.0' can be stored in a seperate table. This way mandatory data can be checked for at the object level (or whatever the data construct dictates).
Besides 'configuration data' I have other data that won't fit in the regular fields of Netbox objects today. My initial thought would be to store json in 'custom fields' (if possible?) and use basic inheritence to end up with a complete json object at the requested object.
Then you proposed 'configcontext as a seperate model' and I thought that would be a great addition to 'custom fields' as well to fascilitate for the more complex composition of 'custom fields' data from multiple Netbox objects. Adding the two together made me think: we should store the 'custom fields' and 'configcontext' as a seperate model that can fasciltiate both use cases and more.
@jeremystretch commented on GitHub (Jun 29, 2018):
Work on this feature has been merged into
develop-2.4.