pynetbox icon indicating copy to clipboard operation
pynetbox copied to clipboard

Permission Constraints are handled as Records

Open moonrail opened this issue 2 years ago • 2 comments

Follow up to #398, as this issue still persists with current pynetbox:

2022-05-20T16:31:32Z DEBUG    Type of permission.constraints: <class 'list'> 
2022-05-20T16:31:32Z DEBUG    Type of first item in permission.constraints: <class 'pynetbox.core.response.Record'> 
Traceback (most recent call last):
(...)
  File "/home/jg/.local/lib/python3.10/site-packages/pynetbox/core/response.py", line 484, in _diff
    {fmt_dict(k, v) for k, v in self.serialize(init=True).items()}
  File "/home/jg/.local/lib/python3.10/site-packages/pynetbox/core/response.py", line 463, in serialize
    current_val = [
  File "/home/jg/.local/lib/python3.10/site-packages/pynetbox/core/response.py", line 464, in <listcomp>
    v.id if isinstance(v, Record) else v for v in current_val
  File "/home/jg/.local/lib/python3.10/site-packages/pynetbox/core/response.py", line 298, in __getattr__
    raise AttributeError('object has no attribute "{}"'.format(k))
AttributeError: object has no attribute "id"

I think this bit of code has to be reworked for it to work: https://github.com/netbox-community/pynetbox/blob/v6.6.2/pynetbox/core/response.py#L369

As NetBox returns constraints as a list, isinstance(v, dict) will never be true, but isinstance(v, list) will: https://github.com/netbox-community/pynetbox/blob/v6.6.2/pynetbox/core/response.py#L384

Then each item will be converted to a Record, as Permissions.constraints is not a list, but JsonField: https://github.com/netbox-community/pynetbox/blob/v6.6.2/pynetbox/core/response.py#L360 https://github.com/netbox-community/pynetbox/blob/v6.6.2/pynetbox/models/users.py#L26

As JsonField does not take any arguments, the solution of users is not usable without further changes: https://github.com/netbox-community/pynetbox/blob/v6.6.2/pynetbox/models/users.py#L25 https://github.com/netbox-community/pynetbox/blob/v6.6.2/pynetbox/core/response.py#L366

moonrail avatar May 20 '22 15:05 moonrail

Please provide a minimum reproducable example of the situation (what did you try to do with what kind of input etc), that will hugely help someone to start tackling the problem. Thanks! 👍

markkuleinio avatar May 20 '22 16:05 markkuleinio

Here you go, based on documented examples by NetBox maintainers: https://docs.netbox.dev/en/stable/administration/permissions/#constraints

import pynetbox

def ensure_permission(api, permission):
    nb_permission = api.users.permissions.get(name=permission['name'])
    if nb_permission is None:
        print(f'Creating permission {permission["name"]}')
        return api.users.permissions.create(permission)
    
    # for the sake of a simple example, only constraint updating is implemented
    nb_permission.constraints = permission['constraints']

    print(f'Saving permission {permission["name"]}')
    nb_permission.save()
    return nb_permission

def delete_permission(api, permission):
    nb_permission = api.users.permissions.get(name=permission['name'])
    if nb_permission is not None:
        print(f'Deleting permission {permission["name"]}')
        nb_permission.delete()

api = pynetbox.api(
    'https://netbox.domain.tld',
    token='<token>'
)

permissions = [
    {
        'name': 'nb_example_constraint_1',
        'enabled': False,
        'object_types': [
            'dcim.site'
        ],
        'actions': [
            'add',
            'change',
            'delete'
        ],
        'constraints': {
            "status": "active",
            "region__name": "Americas"
        }
    },
    {
        'name': 'nb_example_constraint_2',
        'enabled': False,
        'object_types': [
            'ipam.vlan'
        ],
        'actions': [
            'add',
            'change',
            'delete'
        ],
        'constraints': [
            {
                "vid__gte": 100,
                "vid__lt": 200
            },
            {

                "status": "reserved"
            }
        ]
    }
]

print('Cleaning up permissions from previous runs')
for permission in permissions:
    delete_permission(api, permission)

print('Creating permissions')
for permission in permissions:
    ensure_permission(api, permission)

# do the same again - it should be idempotent (Hint: its not for nb_example_constraint_2)
print('Doing an idempotency check')
for permission in permissions:
    ensure_permission(api, permission)

# now change constraints on both with other NetBox documentation examples:
permissions[0]['constraints']['name__startswith'] = 'Foo'
permissions[1]['constraints'].append({
    'role': 'testing'
})

print('Updating permissions')
for permission in permissions:
    ensure_permission(api, permission)

moonrail avatar May 20 '22 17:05 moonrail

Hi! I just checked and sadly have to confirm that the issue still exists. Any chance this will get a fix?

Regards, j.

jhofmueller avatar May 02 '23 08:05 jhofmueller