Generating Full Device Configurations

Hello again!

I have posted before with regard to generating individual sections of a device’s configuration (e.g. load just the interfaces section of a junos device). I have a number of these tasks loading different parts of a configuration, which work just fine, but now I’d like to build upon this concept.

What would be the best way to load an entire configuration for a device? Currently, I’m running napalm_configure within each task based on the results of the template_file subtask. I’d like to avoid having the script log into the device 15 times, so it makes more sense to combine the results of each task into one data structure and run napalm_configure once. Is this the right approach? Hopefully that makes sense, and I can clarify as needed. Thank you!

I would just render a template that includes all of your “sub templates”, then just push that config.

Seems like there isn’t a real benefit outside of testing/playing around to generating a bunch of “sub sections” – if the goal is just keeping the templates smaller/cleaner include seems like that solves that problem.

Thanks @carl. Indeed, I was requested to keep templates smaller/cleaner and be able to apply configs at a more granular level. Here I was trying to combine aggregated results together without thinking of combining at the template level. Thank you!!

Hi again,

If this warrants a separate post, please let me know. In working through this process with includes in the jinja templates, which work just fine btw, I noticed myself coming back to how I order the yaml data in the first place.

I want to create the same hierarchy that ansible provides with regard to host and group variables. Basically, key/values that are listed in group or global files are overwritten if the same pairs are listed in host files. So far, I’ve been playing with dictionaries and trying to update keys using update() sequentially (global -> group -> host, where host variables override all). There are certain keys that I don’t want to override and instead iterate through the next level, which at this point is kinda over my head. I feel like I’m making this harder than it needs to be, but any guidance is definitely appreciated!

Hey,

I have done something similar where keys in the group_vars get overridden by the same key in host_vars. I looked to do something like this since I used Ansible before taking Nornir our for a spin.

Here I mention a bit about it: https://github.com/nornir-automation/nornir/issues/238#issuecomment-442658605

Thank you! I remember seeing this post throughout my trek and have gotten something very similar going now. We have mostly Juniper devices, and I have the yaml files organized by top-level keys based on how Junos has its hierarchy. So, like, each host/group/default file can have something like:

system:
  <some data>

interfaces:
  <some data>

protocols:
  <some data>

...and so on...

What I noticed when trying to merge dictionaries from these files is that anything within these top-level keys gets overwritten. Anything under system, for instance, is overwritten by the last file loaded (in this case, the host file). Is there any way to merge key/values under these higher-level keys but have them be overwritten based on host/group/default? I’m hoping that makes sense. For example:

in group_vars/edge.yaml

system:
  dns_server: <group.dns.server>
  ntp_server: <group.ntp.server>

and in host_vars/edge-rtr-1.yam

system:
  dns_server: <host.dns.server>

After merging, task.host.data for edge-rtr-1 should be like:

system:
  dns_server: <host.dns.server>
  ntp_server: <group.ntp.server>

Does this approach to organizing the data make sense? Is there a better way to do it? Much appreciated!

Hey, if I’m reading this correctly, that’s exactly what the link I posted does. Is “edge-rtr-1” configured as an “edge” in the inventory? Something like this:

edge-rtr-1:
  hostname: "edge-rtr-1"
  groups:
    - "junos"
  data:
    role: "edge"

That’s exactly how I have it set up. Hrmm, I’ll have to try this again then.

Okay so I tried to mimic this using simple dictionaries and am getting the same thing:

group_vars = {'group': {'site': 'east'}, 'system': {'domain': 'test.com', 'dns_server': '1.1.1.1'}}
host_vars = {'system': {'hostname': 'testhost', 'dns_server': '8.8.8.8'}}
result = {**group_vars, **host_vars}

print(result)                                                                                                                                                                                                                                                                    
{'group': {'site': 'east'}, 'system': {'hostname': 'testhost', 'dns_server': '8.8.8.8'}}

So result[‘system’] only contains what is in host_vars[‘system’], which overwrites group_vars[‘system’] entirely. Ideally, I’d want result[‘system’] to look like:

{'group': {'site': 'east'}, 'system': {'domain': 'test.com', 'hostname': 'testhost', 'dns_server': '8.8.8.8'}}

The primary goal for using high-level keys like system is to provide better organization for others reading the yaml files. However, if there is a better way to do this, I’m all for it.

To a degree, I do want to be able to overwrite certain keys. For instance, I want host_vars[‘system’][‘dns_server’] to overwrite group_vars[‘system’][‘dns_server’]; I think it’s going to be a matter of peeling back that top-level key before doing the merge, but I’m not sure yet how best to do that.

I see this working as intended and not sure how to accomplish what you’re looking to do. I do take advantage of defaults.yaml for things that should be in every host, like domain.

---

data:
  domain: "test.com"
  snmp_contact: "netops@test.com"

Thank you again for the input. I probably need to rework how I organize data in general, which I’m currently working through. I might make use of putting the defaults into the hosts/groups though, good call!