Skip to content

FR: Access Netbox Contact Role #50

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
kuhball opened this issue Apr 24, 2025 · 12 comments
Open

FR: Access Netbox Contact Role #50

kuhball opened this issue Apr 24, 2025 · 12 comments

Comments

@kuhball
Copy link

kuhball commented Apr 24, 2025

Hi, thanks for developing this great module!

I'm currently trying to model contact integration from netbox into icinga. We're distinguishing between on call and regular contacts (sms vs mail) and it's possible to assign a contact role within netbox. The current contact assignment is not syncing this to the icinga host object.

I tried modeling this with a separate import source importing the contact assignments. There I'm using the hostname of the device as keyid to be able to merge the host object data between the two sources. This breaks when you're assigning more than 1 contact to a device (duplicated key).

Are there any other smart ways to merge the contact assignment to a device including the contact role? Would it be feasible to include to contact role from the assignment within the module?

@sol1-matt
Copy link
Member

@kuhball The way I envisioned contacts being setup was to create a list of contact names which can then be added as a var to the host

The imported vars are contact_keyids and contacts.
The icinga host var would be something like this

    var.contacts = [
        "John Doe",
        "Jane Doe"
    ]

The notification apply rules can be triggered using list contains.

No roles here, but we could extend this so that we create one var per role so if you had the roles on call and helpdesk we'd end up with the following columns in the import source

  • All contacts
    • contacts
    • contact_keyids
  • On Call contacts only
    • contacts_on_call
    • contacts_on_call_keyids
  • Helpdesk contacts only
    • contact_helpdesk
    • contact_helpdesk_keyids

The separate contact vars then get added to hosts and read for separate notification apply rules.

The only downside I can see with this is if there were a bunch of unrelated roles, then we creating a bunch of columns that we don't really need.

It might make more sense to add a constraint so that we only import specific roles.

Would this work for your use case?

@sol1-matt
Copy link
Member

A quick look at the code and netbox reveals that we don't have any additional api call cost to doing this, still have additional columns of data in the import source which fills up the director db but no major blockers.

The above solution should be fairly straight forward case of

  • new UI to select which contact roles to import
  • update function get_contact_assignments to create additional columns based on UI selection

@kuhball
Copy link
Author

kuhball commented May 5, 2025

I was thinking of something like this:

    var.contacts = [
        {"name": "John Doe", "role": "oncall"},
        {"name": "Jane Doe", "role": "helpdesk"},
    ]

This of course is a breaking change. Your solution with an additional constraint for roles would work for our usecase.

@sol1-matt
Copy link
Member

It wouldn't be hard to create that structure without breaking anything, just create a new column in the import contact_dict.

Have you tested how the notifications are made and work using this structure as it's starting point?

I know when I originally got contacts working the only real way to automate both who should be notified (the host var) and the notification rules themselves was to pull the contact assignments onto the host and then create one notification apply rule per contact.

@kuhball
Copy link
Author

kuhball commented May 8, 2025

We're actually using these scripts for notifications. All i do is put a list of contacts into a var on the host / service and notifications work. So for this use case any of the provided solutions are fine.

@sol1-matt
Copy link
Member

sol1-matt commented May 8, 2025

tentative solution will be to create a new dict var contacts_dict here which contains

  • contact name
  • contact keyid
  • contact role
  • contact group (because Netbox 4.3 allows for multiple group memberships per contact)

@sol1-matt
Copy link
Member

* contact group (because Netbox 4.3 allows for multiple group memberships per contact)

while the contact can have multiple group memberships the contact assignment doesn't actually have the group on it, so the first available group is selected in the UI and the api has nothing.

@sol1-matt
Copy link
Member

added branch 50-contact-role with simple commit ready for testing.

This change may not be useable without custom notification scripts that parse the data and generating per role vars is a better solution. Validation of use required.

@kuhball if you test this and get it working the way you'd I'd be interested in seeing your Icinga notification config.

@kuhball
Copy link
Author

kuhball commented May 13, 2025

I started testing and am a bit frustrated by the modifiers within icinga. It seems impossible to me to transform an array of dicts to a list of strings. Filtering based on the role works with the Filter Array Values modifier. But using the get a specific array element doesn't work on an array of dicts but only on single dicts.

To not take up more of your time I would offer to build a PR following your approach with having a var per role. This is littering the database more than the current approach, but I don't see another way without touching the modifiers (looking at the director repo I don't know how much traction there is and if they would be willing to merge this).

@sol1-matt
Copy link
Member

sol1-matt commented May 13, 2025

The modifiers aren't a great help for something like this and part of the point of this module for me is to reduce the need for modifiers, so I'm happy to be coding around the problem.

I'm fine with adding fields in the db, the problem will be figuring out which roles exist to know which field need to be created.
This is an existing problem and the reason for Parse all data for list columns in the options.

The way director works is it uses the module to do load the data twice, once to figure out the columns and once to load the data. By default I have set the list columns function to return 1 row of data, but that won't work here if we expect all roles to have a column so the PR will need to take into account that Parse all data for list columns will be a required selection for this to work.

The alternative will be to add another option that can be used list the contact roles that need fields created for them and then only matching roles will be created. This will bypass the need for Parse all data for list columns. Perhaps a text field with comma separated list of strings matching the role names.

edit: Unless you feel otherwise I think a text field listing the roles to create columns for is the way to go.

Build on 50-contact-role for the PR @kuhball.

@kuhball
Copy link
Author

kuhball commented May 23, 2025

Finally found some time to continue with this and build a solution. I managed to avoid creating dynamic columns but created the following structure:

{
  mail: [
    "user a",
    "user b"
  ],
  sms: [
    "team a"
  ]
}

With this structure it's easy to use the 'Get a specific Array Element' modifier from the director and filter for the role name by key. Within the sync rule the value just has to be inserted into the device.

Current code is in 50-contact-role. This is based on your previous adjustment since I was unsure whether to keep it. Happy to create a PR from this.

@sol1-matt
Copy link
Member

sol1-matt commented May 26, 2025

I like to write things out to make sure I cover all the bits

We have a static new column so we don't need preload the data to create columns if the first row doesn't have any data

$thing->contact_role_dicts = array();

each row gets a dict with keys, each key is a role name and matches all roles all the time which is no different to the existing columns. The value is a list of contacts with that role.

Extracting the roles needed in notifications requires import modifiers and missing keys can result in a NULL value which is safe (which I try and code around to make config easier but in this case it seems like a good balance).

...

I don't see anything wrong with this approach.

The only thing it doesn't do is expose the keyid for the contact, off the top of my head this means the contact object can't use keyid for it's object name (keyid fixes they "jonny drop tables;" problem)

I think maybe double up on the role -> name/keyid mapping with something like this

array_push($thing->contact_role_dicts[$role_name], $name);
array_push($thing->contact_role_dicts[$role_name . " keyid"], $keyid);

And remove the contact_dicts I did as your implementation seems like a direct replacement but better structured for Icinga to use.

$thing->contact_dicts = array();

...

I'm assuming you have a use case for this so if you want to implement the code on your end, test that it works and submit a PR based on the tested code I'll put out a new version for it.

Please let me know which version of Netbox you are testing this against when you do this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants