Skip to content
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

NetBox v3.7 #130

Merged
merged 7 commits into from
Feb 29, 2024
Merged

NetBox v3.7 #130

merged 7 commits into from
Feb 29, 2024

Conversation

snaselj
Copy link
Contributor

@snaselj snaselj commented Feb 8, 2024

Closes: NaN

What's Changed

  • Added NetBox v3.7 tests.
  • Improved the documentation.

To Do

To see changes between 3.6 and 3.7 NetBox versions it's possible to compare the following files, e.g. with git:

https://github.com/nautobot/nautobot-app-netbox-importer/blob/u/snaselj-napps-204-netbox-v3.7/nautobot_netbox_importer/tests/fixtures/3.6/summary.txt
https://github.com/nautobot/nautobot-app-netbox-importer/blob/u/snaselj-napps-204-netbox-v3.7/nautobot_netbox_importer/tests/fixtures/3.7/summary.txt

It's also possible to compare structutred JSON summaries as well.

@snaselj snaselj changed the base branch from develop to u/snaselj-napps-219-summary February 8, 2024 08:23
@hchybz
Copy link

hchybz commented Feb 8, 2024

Hi, we have a beautiful 3.7.1 NB db ready to test with your new importer ..

migration from NB to Nautobot is under discussions
image

@snaselj
Copy link
Contributor Author

snaselj commented Feb 8, 2024

Hi, we have a beautiful 3.7.1 NB db ready to test with your new importer ..

migration from NB to Nautobot is under discussions

Hi @hchybz , would be great to be able to test it with your data. I can provide you with a few steps to run the import in your dev environment, if you are interested. Or would you like other way of cooperation?

@hchybz
Copy link

hchybz commented Feb 8, 2024

Hi Jan,

ok to test your steps, with pleasure.

Our dev env is hosted either on our laptops with docker or in a kube cluster. Would be simpler i think to test it with docker.

@snaselj
Copy link
Contributor Author

snaselj commented Feb 9, 2024

Hi Jan,

ok to test your steps, with pleasure.

Our dev env is hosted either on our laptops with docker or in a kube cluster. Would be simpler i think to test it with docker.

Hi, here are instructions to import the NetBox data into the dev Nautobot database.

First, you need to export the data from the NetBox database:

./manage.py dumpdata \
    --traceback \
    --format=json \
    --indent=2 \
    --natural-primary \
    --natural-foreign \
    --exclude=extras.ObjectChange \
    --exclude=extras.Report \
    --exclude=extras.Script \
    --exclude=django_rq.Queue \
    --output=/tmp/netbox_data.json

The most simple way to get Nautobot dev environment with NetBox Importer App running is to use included docker compose based dev environment:

pip install invoke

git clone https://github.com/nautobot/nautobot-app-netbox-importer.git
cd nautobot-app-netbox-importer/
git checkout u/snaselj-napps-204-netbox-v3.7 
cp development/creds.example.env development/creds.env         
invoke build

After the build is done, you can start the dev environment with:

invoke debug -s nautobot

# ... logs ....

nautobot-1  |   Nautobot initialized!

This runs Nautobot and services using docker compose and keeps the logs open. After the services are up and running, you should be able to access Nautobot at http://localhost:8080/ and login with the default credentials admin/admin.

Now, keep the Nautobot running and open a new terminal window. Run the following commands to import the exported data dump into the Nautobot database:

cd nautobot-app-netbox-importer/

# Necessary to copy the exported data to the nautobot-app-netbox-importer directory to access it from the container
cp <path-to-exported>/netbox_data.json ./netbox_data.json

invoke import-netbox \
  --bypass-data-validation \
  --print-summary \
  --print-fields-mapping \
  --save-json-summary-path=mappings.json \
  --save-text-summary-path=mappings.txt \
  --no-dry-run \
  --file=netbox_data.json

The command above produces detailed debug log and mappings files. These mappings files doesn't contain any sensitive data and can be shared with us to help us improve the importer.

If you have any questions or need help, feel free to ask.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@whitej6 This file shows diff in field mappings from 3.6 => 3.7. Who can help to identify how to map skipped fields?

NO IMPORTER => NO TARGET

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you can see the diff between 3.6 and 3.7:

54f8a51

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@snaselj snaselj changed the base branch from u/snaselj-napps-219-summary to develop February 9, 2024 13:26
@hchybz
Copy link

hchybz commented Feb 10, 2024

Hi Jan,

one of my devs, aka Helder, tested your importer yesterday. We arrived at a point where half-U devices are not recognized by the importer

I never understood why NB team has implemented that feature since what we need in DC is not half-height devices but half-width.

For one of our sites we have a an HPC cluster with half-width 100G Infiniband switches on top of racks.. My colleague implemented that in NB using the half height feature.

image

the device type

image

on out dev NB, we gona delete those half U devices to check wether the import process will finish or not

Is there a way to implement correctly half width devices in nautobot ?

/BR
Thx

Copy link
Contributor

@glennmatthews glennmatthews left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you highlight which parts of this diff are specifically relevant to adding NetBox 3.7 support? I see a lot of refactoring and a lot of API changes but are there no actual functional changes required to support 3.7?

@snaselj
Copy link
Contributor Author

snaselj commented Feb 12, 2024

Can you highlight which parts of this diff are specifically relevant to adding NetBox 3.7 support? I see a lot of refactoring and a lot of API changes but are there no actual functional changes required to support 3.7?

It's still a draft, will be cleaned up before marking ready for review.

@hchybz
Copy link

hchybz commented Feb 12, 2024

We found another issue, related to a "new" feature in NB : "Custom Field Choices"..

importer new error: ValueError: Choices must be a list of strings, got <class 'int'> 
15:24:36.397 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Importing record SourceModelWrapper<extras.customfield -> extras.customfield> {'created': '2022-05-30T00:00:00Z', 'last_updated': '2022-05-30T16:42:08.724Z', 'type': 'select', 'object_type': None, 'name': 'osk_region', 'label': 'Openstack region', 'group_name': '', 'description': '', 'required': False, 'search_weight': 1000, 'filter_logic': 'exact', 'default': None, 'weight': 100, 'validation_minimum': None, 'validation_maximum': None, 'validation_regex': '', 'choice_set': 11, 'ui_visible': 'always', 'ui_editable': 'yes', 'is_cloneable': False, 'content_types': [['dcim', 'device']], 'id': 17}

my dev guy think it is the field : 'choice_set': 11

From what I can see in NB (and what I know about my OSK infra :-) ) .. "Openstack region" is clearly a string,

PERSO 508b

When I click on "osk_region Choices". ..it is choice #11 as seen in URL.. looks like if link was not followed.
(see URL /custom-field-choices/11)

PERSO 509b

@@ -8199,9 +8813,26 @@
}
}
},
"extras.customfieldchoiceset": {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this different from Nautobot's extras.customfieldchoice model?

Copy link
Contributor Author

@snaselj snaselj Feb 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's different.

NetBox allows to define single CustomFieldChoiceSet with multiple choices, and assign it to multiple CustomFields.

Nautobot allows to define multiple CustomFieldChoice and each assign to the single CustomField.

Implemented here: #131

@@ -8669,6 +9338,18 @@
"default_value": null,
"disable_reason": ""
},
"object_types": {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this differ from the content_types field?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to be the same, mapped

"users.adminuser": {
"content_type": "users.adminuser",
"content_type_id": 103,
"users.netboxuser": {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this map to Nautobot's users.user?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like it's just a proxy model without any data in the DB.

@@ -120,9 +120,18 @@ fcb61481-0da8-5412-b68b-2463016a017d P2-12B | ['Rack R204 (Row 2) and power pane
Total validation issues: 48
- Content Types Mapping Deviations: ----------------------------------------------------------------
Mapping deviations from source content type to Nautobot content type
account.usertoken => account.usertoken | Disabled with reason: Nautobot content type: `account.usertoken` not found
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this different from Nautobot's users.token?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to import user tokens from NetBox to Nautobot? I would rather skip importing passwords and tokens.

@snaselj
Copy link
Contributor Author

snaselj commented Feb 14, 2024

We found another issue, related to a "new" feature in NB : "Custom Field Choices"..

importer new error: ValueError: Choices must be a list of strings, got <class 'int'> 
15:24:36.397 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Importing record SourceModelWrapper<extras.customfield -> extras.customfield> {'created': '2022-05-30T00:00:00Z', 'last_updated': '2022-05-30T16:42:08.724Z', 'type': 'select', 'object_type': None, 'name': 'osk_region', 'label': 'Openstack region', 'group_name': '', 'description': '', 'required': False, 'search_weight': 1000, 'filter_logic': 'exact', 'default': None, 'weight': 100, 'validation_minimum': None, 'validation_maximum': None, 'validation_regex': '', 'choice_set': 11, 'ui_visible': 'always', 'ui_editable': 'yes', 'is_cloneable': False, 'content_types': [['dcim', 'device']], 'id': 17}

Hi @hchybz , thanks for reporting this. Importing CustomFieldChoiceSets from NetBox to Nautobot is fixed in the latest commits to this branch.

@snaselj
Copy link
Contributor Author

snaselj commented Feb 14, 2024

Hi Jan,

one of my devs, aka Helder, tested your importer yesterday. We arrived at a point where half-U devices are not recognized by the importer

I never understood why NB team has implemented that feature since what we need in DC is not half-height devices but half-width.

For one of our sites we have a an HPC cluster with half-width 100G Infiniband switches on top of racks.. My colleague implemented that in NB using the half height feature.

image

the device type

image

on out dev NB, we gona delete those half U devices to check wether the import process will finish or not

Is there a way to implement correctly half width devices in nautobot ?

/BR Thx

Thanks for testing this. Nautobot currently supports only integer values for U heights. Will improve the importer to be able to import such devices, but there will be possibly some data loss.

@hchybz
Copy link

hchybz commented Feb 16, 2024

@snaselj to be able to continue testing, we will change/delete assets that are racks in "half height" positions.
My dev guy (@helderbetiol) is following your posts to continue testing

@snaselj
Copy link
Contributor Author

snaselj commented Feb 16, 2024

@snaselj to be able to continue testing, we will change/delete assets that are racks in "half height" positions. My dev guy (@helderbetiol) is following your posts to continue testing

Hi @hchybz and @helderbetiol,

I added some improvements, u_height field is now trunctated to the integer value. Not sure yet, about rack assignment, will test it, and if neccessary implement un-racking devices that would fail.

@helderbetiol
Copy link

Hi @snaselj ,

Thank you for the improvements! We are now facing the following issue:

14:41:05.335 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Importing record SourceModelWrapper<users.objectpermission -> users.objectpermission> {'name': 'extras.run_scripts', 'description': 'Run Custom Scripts', 'enabled': False, 'actions': '["run", "view"]', 'constraints': None, 'object_types': [['extras', 'script']], 'groups': [], 'users': [['admin']], 'id': 1}
14:41:05.336 DEBUG   nautobot-netbox-importer nautobot.py                     diffsync_class() :
  Created DiffSync Model {'__annotations__': {'id': <class 'uuid.UUID'>, 'name': typing.Optional[str], 'description': typing.Optional[str], 'enabled': typing.Optional[bool], 'actions': typing.Optional[typing.Any], 'constraints': typing.Optional[typing.Any], 'object_types': typing.Optional[typing.Set[int]], 'groups': typing.Optional[typing.Set[int]], 'users': typing.Optional[typing.Set[uuid.UUID]]}, '_attributes': ['name', 'description', 'enabled', 'actions', 'constraints', 'object_types', 'groups', 'users'], '_identifiers': ['id'], '_modelname': 'users_objectpermission', '_wrapper': <nautobot_netbox_importer.generator.nautobot.NautobotModelWrapper object at 0xffff8f71e1d0>, 'id': FieldInfo(default=PydanticUndefined, extra={}), 'name': FieldInfo(extra={}), 'description': FieldInfo(extra={}), 'enabled': FieldInfo(extra={}), 'actions': FieldInfo(extra={}), 'constraints': FieldInfo(extra={}), 'object_types': FieldInfo(extra={}), 'groups': FieldInfo(extra={}), 'users': FieldInfo(extra={})}
Traceback (most recent call last):
  File "/usr/local/bin/nautobot-server", line 8, in <module>
    sys.exit(main())
             ^^^^^^
  File "/usr/local/lib/python3.11/site-packages/nautobot/core/cli/__init__.py", line 53, in main
    run_app(
  File "/usr/local/lib/python3.11/site-packages/nautobot/core/runner/runner.py", line 297, in run_app
    management.execute_from_command_line([runner_name, command] + command_args)
  File "/usr/local/lib/python3.11/site-packages/django/core/management/__init__.py", line 419, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python3.11/site-packages/django/core/management/__init__.py", line 413, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python3.11/site-packages/django/core/management/base.py", line 354, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/usr/local/lib/python3.11/site-packages/django/core/management/base.py", line 398, in execute
    output = self.handle(*args, **options)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/source/nautobot_netbox_importer/management/commands/import_netbox.py", line 88, in handle
    adapter.import_to_nautobot()
  File "/source/nautobot_netbox_importer/diffsync/adapters/netbox.py", line 86, in import_to_nautobot
    self._atomic_import()
  File "/usr/local/lib/python3.11/contextlib.py", line 81, in inner
    return func(*args, **kwds)
           ^^^^^^^^^^^^^^^^^^^
  File "/source/nautobot_netbox_importer/diffsync/adapters/netbox.py", line 103, in _atomic_import
    self.load()
  File "/source/nautobot_netbox_importer/diffsync/adapters/netbox.py", line 77, in load
    self.import_data()
  File "/source/nautobot_netbox_importer/generator/source.py", line 281, in import_data
    wrapper.import_record(data)
  File "/source/nautobot_netbox_importer/generator/source.py", line 536, in import_record
    importer(data, target)
  File "/source/nautobot_netbox_importer/generator/source.py", line 936, in content_types_importer
    setattr(target, self.nautobot.name, set(adapter.get_nautobot_content_type_uid(item) for item in values))
                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/source/nautobot_netbox_importer/generator/source.py", line 936, in <genexpr>
    setattr(target, self.nautobot.name, set(adapter.get_nautobot_content_type_uid(item) for item in values))
                                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/source/nautobot_netbox_importer/generator/source.py", line 239, in get_nautobot_content_type_uid
    return wrapper.nautobot.content_type_instance.pk
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/source/nautobot_netbox_importer/generator/nautobot.py", line 414, in content_type_instance
    self._content_type_instance = ContentType.objects.get_for_model(self.model)
                                                                    ^^^^^^^^^^
  File "/source/nautobot_netbox_importer/generator/nautobot.py", line 270, in model
    raise NotImplementedError("Cannot use disabled model")
NotImplementedError: Cannot use disabled model

Any ideas on how to fix it?

@snaselj
Copy link
Contributor Author

snaselj commented Feb 19, 2024

Hi @snaselj ,

Thank you for the improvements! We are now facing the following issue:

14:41:05.335 DEBUG   nautobot-netbox-importer source.py                        import_record() :
...
    raise NotImplementedError("Cannot use disabled model")
NotImplementedError: Cannot use disabled model

Any ideas on how to fix it?

Hi @helderbetiol

pushed a quick fix, will clean it up yet but should fix this issue now.

If anything else, let me know please.

@helderbetiol
Copy link

Hi @helderbetiol

pushed a quick fix, will clean it up yet but should fix this issue now.

If anything else, let me know please.

Hi @snaselj

Thank you for the quick fix, this time it went a little further but it still encountered an issue soon after:

  Importing record SourceModelWrapper<users.objectpermission -> users.objectpermission> {'name': 'extras.run_scripts', 'description': 'Run Custom Scripts', 'enabled': True, 'actions': '["run", "view"]', 'constraints': None, 'object_types': [['extras', 'script']], 'groups': [], 'users': [['admin']], 'id': 1}
15:54:11.285 DEBUG   nautobot-netbox-importer nautobot.py                     diffsync_class() :
  Created DiffSync Model {'__annotations__': {'id': <class 'uuid.UUID'>, 'name': typing.Optional[str], 'description': typing.Optional[str], 'enabled': typing.Optional[bool], 'actions': typing.Optional[typing.Any], 'constraints': typing.Optional[typing.Any], 'object_types': typing.Optional[typing.Set[int]], 'groups': typing.Optional[typing.Set[int]], 'users': typing.Optional[typing.Set[uuid.UUID]]}, '_attributes': ['name', 'description', 'enabled', 'actions', 'constraints', 'object_types', 'groups', 'users'], '_identifiers': ['id'], '_modelname': 'users_objectpermission', '_wrapper': <nautobot_netbox_importer.generator.nautobot.NautobotModelWrapper object at 0xffff7c2cc3d0>, 'id': FieldInfo(default=PydanticUndefined, extra={}), 'name': FieldInfo(extra={}), 'description': FieldInfo(extra={}), 'enabled': FieldInfo(extra={}), 'actions': FieldInfo(extra={}), 'constraints': FieldInfo(extra={}), 'object_types': FieldInfo(extra={}), 'groups': FieldInfo(extra={}), 'users': FieldInfo(extra={})}
15:54:11.296 WARNING nautobot-netbox-importer source.py               content_types_importer() :
  Can't convert content type ['extras', 'script'] for field users.objectpermission->object_types, skipping
Can't convert content type ['extras', 'script'] for field users.objectpermission->object_types, skipping
15:54:11.296 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Imported c650165f-9a05-55c0-bbb1-c9527d41160c {'name': 'extras.run_scripts', 'description': 'Run Custom Scripts', 'enabled': True, 'actions': ['run', 'view'], 'constraints': None, 'object_types': set(), 'groups': None, 'users': {UUID('2abf7f49-abd2-4606-b88b-8b631768dc1a')}}
15:54:11.296 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Importing record SourceModelWrapper<users.objectpermission -> users.objectpermission> {'name': 'DNS Viewer', 'description': 'View all DNS Data', 'enabled': True, 'actions': '["view"]', 'constraints': None, 'object_types': [['netbox_dns', 'view'], ['netbox_dns', 'nameserver'], ['netbox_dns', 'zone'], ['netbox_dns', 'record']], 'groups': [], 'users': [['admin']], 'id': 2}
15:54:11.297 WARNING nautobot-netbox-importer source.py               content_types_importer() :
  Can't convert content type ['netbox_dns', 'view'] for field users.objectpermission->object_types, skipping
Can't convert content type ['netbox_dns', 'view'] for field users.objectpermission->object_types, skipping
15:54:11.298 WARNING nautobot-netbox-importer source.py               content_types_importer() :
  Can't convert content type ['netbox_dns', 'nameserver'] for field users.objectpermission->object_types, skipping
Can't convert content type ['netbox_dns', 'nameserver'] for field users.objectpermission->object_types, skipping
15:54:11.298 WARNING nautobot-netbox-importer source.py               content_types_importer() :
  Can't convert content type ['netbox_dns', 'zone'] for field users.objectpermission->object_types, skipping
Can't convert content type ['netbox_dns', 'zone'] for field users.objectpermission->object_types, skipping
15:54:11.298 WARNING nautobot-netbox-importer source.py               content_types_importer() :
  Can't convert content type ['netbox_dns', 'record'] for field users.objectpermission->object_types, skipping
Can't convert content type ['netbox_dns', 'record'] for field users.objectpermission->object_types, skipping
15:54:11.298 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Imported 8ba06692-8cd2-57e8-bee5-cf96907df4c7 {'name': 'DNS Viewer', 'description': 'View all DNS Data', 'enabled': True, 'actions': ['view'], 'constraints': None, 'object_types': set(), 'groups': None, 'users': {UUID('2abf7f49-abd2-4606-b88b-8b631768dc1a')}}
15:54:11.304 DEBUG   nautobot-netbox-importer nautobot.py                     diffsync_class() :
  Created DiffSync Model {'__annotations__': {'id': <class 'uuid.UUID'>, 'content_types': typing.Optional[typing.Set[int]], 'name': typing.Optional[str]}, '_attributes': ['content_types', 'name'], '_identifiers': ['id'], '_modelname': 'extras_status', '_wrapper': <nautobot_netbox_importer.generator.nautobot.NautobotModelWrapper object at 0xffff7bf33f10>, 'id': FieldInfo(default=PydanticUndefined, extra={}), 'content_types': FieldInfo(extra={}), 'name': FieldInfo(extra={})}
15:54:11.304 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Importing record SourceModelWrapper<extras.status -> extras.status> {'name': 'Unknown'}
15:54:11.305 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Imported 623acb70-22ce-5f69-bd7e-76bc921aa6c4 {'content_types': None, 'name': 'Unknown'}
15:54:11.309 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Importing record SourceModelWrapper<extras.status -> extras.status> {'name': 'Connected'}
15:54:11.310 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Imported 318a7220-4327-48ca-876c-d1389a81758c {'content_types': None, 'name': 'Connected'}
15:54:11.321 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Importing record SourceModelWrapper<extras.status -> extras.status> {'name': 'Planned'}
15:54:11.322 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Imported bbcf35c3-8f8b-476e-b0b6-3221a67c3e24 {'content_types': None, 'name': 'Planned'}
15:54:11.324 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Importing record SourceModelWrapper<extras.status -> extras.status> {'name': 'Active'}
15:54:11.324 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Imported a017f7b3-b263-424d-b12b-c4a1c854d71f {'content_types': None, 'name': 'Active'}
15:54:11.327 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Importing record SourceModelWrapper<extras.status -> extras.status> {'name': 'Inventory'}
15:54:11.328 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Imported a0e998a7-bdf8-4b25-8c00-c8106ae0a8c7 {'content_types': None, 'name': 'Inventory'}
15:54:11.328 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Importing record SourceModelWrapper<extras.status -> extras.status> {'name': 'Offline'}
15:54:11.329 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Imported e51e9e6d-38c3-4904-a224-a69c61cf0661 {'content_types': None, 'name': 'Offline'}
15:54:11.330 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Importing record SourceModelWrapper<extras.status -> extras.status> {'name': 'Staged'}
15:54:11.331 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Imported b41452d2-da87-4b11-a707-6c16e253f3fa {'content_types': None, 'name': 'Staged'}
15:54:11.332 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Importing record SourceModelWrapper<extras.status -> extras.status> {'name': 'Failed'}
15:54:11.332 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Imported 05bdfaae-f070-4084-b616-679210b9533d {'content_types': None, 'name': 'Failed'}
15:54:11.334 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Importing record SourceModelWrapper<extras.status -> extras.status> {'name': 'Decommissioning'}
15:54:11.335 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Imported dd9b118c-1eeb-41c4-8764-6f0719012815 {'content_types': None, 'name': 'Decommissioning'}
15:54:11.338 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Importing record SourceModelWrapper<extras.status -> extras.status> {'name': 'Reserved'}
15:54:11.339 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Imported 57dd310d-e9d9-4ff3-a2f2-52115af02127 {'content_types': None, 'name': 'Reserved'}
15:54:11.341 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Importing record SourceModelWrapper<extras.status -> extras.status> {'name': 'Available'}
15:54:11.342 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Imported 296b854f-3ade-4b5c-97c5-339b5b3d643f {'content_types': None, 'name': 'Available'}
15:54:11.343 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Importing record SourceModelWrapper<extras.status -> extras.status> {'name': 'Deprecated'}
15:54:11.344 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Imported 31b86812-1837-4472-b532-333488af9823 {'content_types': None, 'name': 'Deprecated'}
15:54:11.345 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Importing record SourceModelWrapper<extras.status -> extras.status> {'name': 'Dhcp'}
15:54:11.346 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Imported a6362406-11d4-5516-a8aa-5079df89e997 {'content_types': None, 'name': 'Dhcp'}
15:54:11.976 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Importing record SourceModelWrapper<dcim.manufacturer -> dcim.manufacturer> {'id': 'Unknown', 'name': 'Unknown'}
15:54:11.977 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Imported f5136724-0984-5e3b-b073-ea6d83116997 {'name': 'Unknown', 'created': None, 'last_updated': None, 'custom_field_data': None, 'description': None}
Traceback (most recent call last):
  File "/usr/local/bin/nautobot-server", line 8, in <module>
    sys.exit(main())
             ^^^^^^
  File "/usr/local/lib/python3.11/site-packages/nautobot/core/cli/__init__.py", line 53, in main
    run_app(
  File "/usr/local/lib/python3.11/site-packages/nautobot/core/runner/runner.py", line 297, in run_app
    management.execute_from_command_line([runner_name, command] + command_args)
  File "/usr/local/lib/python3.11/site-packages/django/core/management/__init__.py", line 419, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python3.11/site-packages/django/core/management/__init__.py", line 413, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python3.11/site-packages/django/core/management/base.py", line 354, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/usr/local/lib/python3.11/site-packages/django/core/management/base.py", line 398, in execute
    output = self.handle(*args, **options)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/source/nautobot_netbox_importer/management/commands/import_netbox.py", line 88, in handle
    adapter.import_to_nautobot()
  File "/source/nautobot_netbox_importer/diffsync/adapters/netbox.py", line 94, in import_to_nautobot
    self._atomic_import()
  File "/usr/local/lib/python3.11/contextlib.py", line 81, in inner
    return func(*args, **kwds)
           ^^^^^^^^^^^^^^^^^^^
  File "/source/nautobot_netbox_importer/diffsync/adapters/netbox.py", line 111, in _atomic_import
    self.load()
  File "/source/nautobot_netbox_importer/diffsync/adapters/netbox.py", line 88, in load
    self.post_import()
  File "/source/nautobot_netbox_importer/generator/source.py", line 321, in post_import
    while any(wrapper.post_import() for wrapper in self.wrappers.values()):
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/source/nautobot_netbox_importer/generator/source.py", line 321, in <genexpr>
    while any(wrapper.post_import() for wrapper in self.wrappers.values()):
              ^^^^^^^^^^^^^^^^^^^^^
  File "/source/nautobot_netbox_importer/generator/source.py", line 650, in post_import
    instance = self.get_or_create(uid, fail_missing=True)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/source/nautobot_netbox_importer/generator/source.py", line 595, in get_or_create
    raise ValueError(f"Missing {self} {uid} in Nautobot or cached data")
ValueError: Missing SourceModelWrapper<ipam.role -> extras.role> 3463a4e2-b060-554f-a509-eef9e10d030d in Nautobot or cached data

@snaselj
Copy link
Contributor Author

snaselj commented Feb 20, 2024

Hi @helderbetiol
pushed a quick fix, will clean it up yet but should fix this issue now.
If anything else, let me know please.

Hi @snaselj

Thank you for the quick fix, this time it went a little further but it still encountered an issue soon after:

...
  File "/source/nautobot_netbox_importer/generator/source.py", line 595, in get_or_create
    raise ValueError(f"Missing {self} {uid} in Nautobot or cached data")
ValueError: Missing SourceModelWrapper<ipam.role -> extras.role> 3463a4e2-b060-554f-a509-eef9e10d030d in Nautobot or cached data

Hi @helderbetiol ,

Thank you for reporting this issue. The error indicates that some record references ipam.role in NetBox, but the corresponding ipam.role record does not exist. Unfortunately, I cannot reproduce this with my testing dataset.

It's possible that the ipam.role is missing in your JSON dataset exported from NetBox.

I've implemented a temporary fix, though I am still not sure about the root cause. Let me know if the issue persists please.

Anyway, would be helpful if you could check the import log for the UUID 3463a4e2-b060-554f-a509-eef9e10d030d and provide me with the log details for this.

@helderbetiol
Copy link

Hi @snaselj ,

I'm still getting an error even with the most recent change:

17:30:44.254 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Imported f5136724-0984-5e3b-b073-ea6d83116997 {'name': 'Unknown', 'created': None, 'last_updated': None, 'custom_field_data': None, 'description': None}
17:30:44.353 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Importing record SourceModelWrapper<ipam.role -> extras.role> {}
Traceback (most recent call last):
  File "/source/nautobot_netbox_importer/generator/source.py", line 652, in post_import
    instance = self.get_or_create(uid, fail_missing=True)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/source/nautobot_netbox_importer/generator/source.py", line 596, in get_or_create
    raise MissingInstance(f"Missing {self} {uid} in Nautobot or cached data")
nautobot_netbox_importer.generator.exceptions.MissingInstance: Missing SourceModelWrapper<ipam.role -> extras.role> 3463a4e2-b060-554f-a509-eef9e10d030d in Nautobot or cached data

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/bin/nautobot-server", line 8, in <module>
    sys.exit(main())
             ^^^^^^
  File "/usr/local/lib/python3.11/site-packages/nautobot/core/cli/__init__.py", line 53, in main
    run_app(
  File "/usr/local/lib/python3.11/site-packages/nautobot/core/runner/runner.py", line 297, in run_app
    management.execute_from_command_line([runner_name, command] + command_args)
  File "/usr/local/lib/python3.11/site-packages/django/core/management/__init__.py", line 419, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python3.11/site-packages/django/core/management/__init__.py", line 413, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python3.11/site-packages/django/core/management/base.py", line 354, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/usr/local/lib/python3.11/site-packages/django/core/management/base.py", line 398, in execute
    output = self.handle(*args, **options)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/source/nautobot_netbox_importer/management/commands/import_netbox.py", line 88, in handle
    adapter.import_to_nautobot()
  File "/source/nautobot_netbox_importer/diffsync/adapters/netbox.py", line 94, in import_to_nautobot
    self._atomic_import()
  File "/usr/local/lib/python3.11/contextlib.py", line 81, in inner
    return func(*args, **kwds)
           ^^^^^^^^^^^^^^^^^^^
  File "/source/nautobot_netbox_importer/diffsync/adapters/netbox.py", line 111, in _atomic_import
    self.load()
  File "/source/nautobot_netbox_importer/diffsync/adapters/netbox.py", line 88, in load
    self.post_import()
  File "/source/nautobot_netbox_importer/generator/source.py", line 322, in post_import
    while any(wrapper.post_import() for wrapper in self.wrappers.values()):
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/source/nautobot_netbox_importer/generator/source.py", line 322, in <genexpr>
    while any(wrapper.post_import() for wrapper in self.wrappers.values()):
              ^^^^^^^^^^^^^^^^^^^^^
  File "/source/nautobot_netbox_importer/generator/source.py", line 656, in post_import
    self.import_record({}, instance)
  File "/source/nautobot_netbox_importer/generator/source.py", line 555, in import_record
    uid = self.get_pk_from_data(data)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/source/nautobot_netbox_importer/generator/source.py", line 546, in get_pk_from_data
    return self.get_pk_from_uid(data[self.nautobot.pk_field.name])
                                ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
KeyError: 'id'

In the import logs, I see the same UUID being used before:

16:34:00.686 DEBUG   nautobot-netbox-importer source.py                        add_reference() :
  Adding reference from: ipam.ipaddress to: ipam.role 3463a4e2-b060-554f-a509-eef9e10d030d
16:34:00.686 DEBUG   nautobot-netbox-importer source.py                        add_reference() :
  Adding reference from: ipam.ipaddress to: extras.status a017f7b3-b263-424d-b12b-c4a1c854d71f
16:34:00.686 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Imported a002c700-bcb6-5ba7-809f-68478905f420 {'status_id': UUID('a017f7b3-b263-424d-b12b-c4a1c854d71f'), 'role_id': UUID('3463a4e2-b060-554f-a509-eef9e10d030d'), 'created': datetime.datetime(2024, 1, 19, 10, 21, 30, 844000, tzinfo=datetime.timezone.utc), 'last_updated': None, 'custom_field_data': None, 'description': None, 'address': '91.239.56.13/32', 'tenant_id': None, 'nat_inside_id': None, 'dns_name': 'vpnusernoe.exaion.tech'}

@snaselj
Copy link
Contributor Author

snaselj commented Feb 21, 2024

In the import logs, I see the same UUID being used before:

16:34:00.686 DEBUG   nautobot-netbox-importer source.py                        add_reference() :
  Adding reference from: ipam.ipaddress to: ipam.role 3463a4e2-b060-554f-a509-eef9e10d030d
16:34:00.686 DEBUG   nautobot-netbox-importer source.py                        add_reference() :
  Adding reference from: ipam.ipaddress to: extras.status a017f7b3-b263-424d-b12b-c4a1c854d71f
16:34:00.686 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Imported a002c700-bcb6-5ba7-809f-68478905f420 {'status_id': UUID('a017f7b3-b263-424d-b12b-c4a1c854d71f'), 'role_id': UUID('3463a4e2-b060-554f-a509-eef9e10d030d'), 'created': datetime.datetime(2024, 1, 19, 10, 21, 30, 844000, tzinfo=datetime.timezone.utc), 'last_updated': None, 'custom_field_data': None, 'description': None, 'address': '91.239.56.13/32', 'tenant_id': None, 'nat_inside_id': None, 'dns_name': 'vpnusernoe.exaion.tech'}

Hi @helderbetiol ,

Thanks for the report. Could you please provide more details from the import logs? There should be a line Importing record ... before the last messages you've provided. There should be the source data for the ipam.ipaddress record referencing the role. Interested, what's in the role field of that record.

Could you please check, that the ipam.role referenced there, is in exported NetBox JSON source?

@helderbetiol
Copy link

Hi @helderbetiol ,

Thanks for the report. Could you please provide more details from the import logs? There should be a line Importing record ... before the last messages you've provided. There should be the source data for the ipam.ipaddress record referencing the role. Interested, what's in the role field of that record.

Could you please check, that the ipam.role referenced there, is in exported NetBox JSON source?

Hi @snaselj ,

We can see here, in the following lines, what role is being referred:

  Importing record SourceModelWrapper<ipam.ipaddress -> ipam.ipaddress> {'created': '2022-09-02T13:07:57.097Z', 'last_updated': '2023-02-03T06:18:24.923Z', 'custom_field_data': {}, 'description': 'Loopback brnoe01', 'comments': '', 'address': '100.64.0.6/24', 'vrf': None, 'tenant': None, 'status': 'active', 'role': 'loopback', 'assigned_object_type': ['dcim', 'interface'], 'assigned_object_id': 28834, 'nat_inside': None, 'dns_name': '', 'id': 15831}
16:13:28.196 DEBUG   nautobot-netbox-importer source.py                        add_reference() :
  Adding reference from: ipam.ipaddress to: ipam.role 3463a4e2-b060-554f-a509-eef9e10d030d
16:13:28.197 DEBUG   nautobot-netbox-importer source.py                        add_reference() :
  Adding reference from: ipam.ipaddress to: extras.status a017f7b3-b263-424d-b12b-c4a1c854d71f
16:13:28.197 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Imported d8cf1316-1de3-581f-b168-00d637157ee2 {'status_id': UUID('a017f7b3-b263-424d-b12b-c4a1c854d71f'), 'role_id': UUID('3463a4e2-b060-554f-a509-eef9e10d030d'), 'created': datetime.datetime(2022, 9, 2, 13, 7, 57, 97000, tzinfo=datetime.timezone.utc), 'last_updated': None, 'custom_field_data': None, 'description': 'Loopback brnoe01', 'address': '100.64.0.6/24', 'tenant_id': None, 'nat_inside_id': None, 'dns_name': None}
16:13:28.197 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Importing record SourceModelWrapper<ipam.ipaddress -> ipam.ipaddress> {'created': '2022-09-02T13:08:11.405Z', 'last_updated': '2022-09-27T05:29:41.164Z', 'custom_field_data': {}, 'description': 'Loopback brnoe02', 'comments': '', 'address': '100.64.0.7/24', 'vrf': None, 'tenant': None, 'status': 'active', 'role': 'loopback', 'assigned_object_type': ['dcim', 'interface'], 'assigned_object_id': 29068, 'nat_inside': None, 'dns_name': '', 'id': 15832}
16:13:28.197 DEBUG   nautobot-netbox-importer source.py                        add_reference() :
  Adding reference from: ipam.ipaddress to: ipam.role 3463a4e2-b060-554f-a509-eef9e10d030d
16:13:28.197 DEBUG   nautobot-netbox-importer source.py                        add_reference() :
  Adding reference from: ipam.ipaddress to: extras.status a017f7b3-b263-424d-b12b-c4a1c854d71f
16:13:28.197 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Imported 5e63a11a-e45c-561d-aec9-b559910e461d {'status_id': UUID('a017f7b3-b263-424d-b12b-c4a1c854d71f'), 'role_id': UUID('3463a4e2-b060-554f-a509-eef9e10d030d'), 'created': datetime.datetime(2022, 9, 2, 13, 8, 11, 405000, tzinfo=datetime.timezone.utc), 'last_updated': None, 'custom_field_data': None, 'description': 'Loopback brnoe02', 'address': '100.64.0.7/24', 'tenant_id': None, 'nat_inside_id': None, 'dns_name': None}
16:13:28.197 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Importing record SourceModelWrapper<ipam.ipaddress -> ipam.ipaddress> {'created': '2022-09-02T13:09:32.505Z', 'last_updated': '2022-09-02T13:12:52.533Z', 'custom_field_data': {}, 'description': 'Loopback brpcy01', 'comments': '', 'address': '100.64.4.6/24', 'vrf': None, 'tenant': None, 'status': 'active', 'role': 'loopback', 'assigned_object_type': ['dcim', 'interface'], 'assigned_object_id': 28835, 'nat_inside': None, 'dns_name': '', 'id': 15833}
16:13:28.198 DEBUG   nautobot-netbox-importer source.py                        add_reference() :
  Adding reference from: ipam.ipaddress to: ipam.role 3463a4e2-b060-554f-a509-eef9e10d030d
16:13:28.198 DEBUG   nautobot-netbox-importer source.py                        add_reference() :
  Adding reference from: ipam.ipaddress to: extras.status a017f7b3-b263-424d-b12b-c4a1c854d71f
16:13:28.198 DEBUG   nautobot-netbox-importer source.py                        import_record() :
  Imported d4dac019-2ec6-59ea-8a0a-334a6992eef8 {'status_id': UUID('a017f7b3-b263-424d-b12b-c4a1c854d71f'), 'role_id': UUID('3463a4e2-b060-554f-a509-eef9e10d030d'), 'created': datetime.datetime(2022, 9, 2, 13, 9, 32, 505000, tzinfo=datetime.timezone.utc), 'last_updated': None, 'custom_field_data': None, 'description': 'Loopback brpcy01', 'address': '100.64.4.6/24', 'tenant_id': None, 'nat_inside_id': None, 'dns_name': None}

I do not have this ipam.role in the exported Netbox json dump, I just see references to it. And it is not the only one, there are other ipam.roles being referred that are not in the dump. Should it be in the dump? Is there anything we can change in the export command to have it? I'm using the following command (similar to yours, just added exclude image attachment):

./manage.py dumpdata \
    --traceback \
    --format=json \
    --indent=2 \
    --natural-primary \
    --natural-foreign \
    --exclude=extras.ObjectChange \
    --exclude=extras.Report \
    --exclude=extras.Script \
    --exclude=extras.ImageAttachment \
    --exclude=django_rq.Queue \
    --output=/tmp/netbox_data.json

@snaselj
Copy link
Contributor Author

snaselj commented Feb 21, 2024

We can see here, in the following lines, what role is being referred:

  Importing record SourceModelWrapper<ipam.ipaddress -> ipam.ipaddress> {'created': '2022-09-02T13:07:57.097Z', 'last_updated': '2023-02-03T06:18:24.923Z', 'custom_field_data': {}, 'description': 'Loopback brnoe01', 'comments': '', 'address': '100.64.0.6/24', 'vrf': None, 'tenant': None, 'status': 'active', 'role': 'loopback', 'assigned_object_type': ['dcim', 'interface'], 'assigned_object_id': 28834, 'nat_inside': None, 'dns_name': '', 'id': 15831}

This issue is fixed now, thanks for your help @helderbetiol . Let me know pls. whether it works for you or is there any other blocker.

@helderbetiol
Copy link

Hi @snaselj ,

Thanks for the fix, it worked for that issue, now I'm facing an issue with extras.customfields with empty labels ("label": ""):

2024-02-21 16:53:16 [debug    ] Attempting model create        action=create diffs={'+': {'content_types': {9, 3}, 'key': 'base_playbook_completed', 'created': datetime.datetime(2022, 5, 30, 0, 0, tzinfo=datetime.timezone.utc), 'last_updated': None, 'type': 'boolean', 'label': None, 'description': None, 'required': True, 'filter_logic': 'loose', 'default': 0, 'weight': 100, 'validation_minimum': None, 'validation_maximum': None, 'validation_regex': None}} dst=<NautobotAdapter "Nautobot"> flags=<DiffSyncFlags.NONE: 0> model=extras_customfield src=<NetBoxAdapter "NetBox"> unique_id=30b7f5c2-3ef4-571f-9781-bde928c2ce8b
16:53:16.131 ERROR   nautobot-netbox-importer nautobot.py             save_nautobot_instance() :
  Save failed: <class 'nautobot.extras.models.customfields.CustomField'> {'_state': <django.db.models.base.ModelState object at 0xffff9a33e3d0>, 'id': UUID('30b7f5c2-3ef4-571f-9781-bde928c2ce8b'), 'created': datetime.datetime(2024, 2, 21, 16, 53, 16, 120802, tzinfo=<UTC>), 'last_updated': datetime.datetime(2024, 2, 21, 16, 53, 16, 120810, tzinfo=<UTC>), 'grouping': '', 'type': 'boolean', 'label': None, 'key': 'base_playbook_completed', 'description': '', 'required': True, 'filter_logic': 'loose', 'default': False, 'weight': 100, 'validation_minimum': None, 'validation_maximum': None, 'validation_regex': '', 'advanced_ui': False}
Save failed: <class 'nautobot.extras.models.customfields.CustomField'> {'_state': <django.db.models.base.ModelState object at 0xffff9a33e3d0>, 'id': UUID('30b7f5c2-3ef4-571f-9781-bde928c2ce8b'), 'created': datetime.datetime(2024, 2, 21, 16, 53, 16, 120802, tzinfo=<UTC>), 'last_updated': datetime.datetime(2024, 2, 21, 16, 53, 16, 120810, tzinfo=<UTC>), 'grouping': '', 'type': 'boolean', 'label': None, 'key': 'base_playbook_completed', 'description': '', 'required': True, 'filter_logic': 'loose', 'default': False, 'weight': 100, 'validation_minimum': None, 'validation_maximum': None, 'validation_regex': '', 'advanced_ui': False}
Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django_prometheus/db/common.py", line 69, in execute
    return super().execute(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
psycopg2.errors.NotNullViolation: null value in column "label" of relation "extras_customfield" violates not-null constraint
DETAIL:  Failing row contains (30b7f5c2-3ef4-571f-9781-bde928c2ce8b, boolean, null, , t, loose, false, 100, null, null, , 2024-02-21 16:53:16.120802+00, 2024-02-21 16:53:16.12081+00, f, base_playbook_completed, ).


The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/local/bin/nautobot-server", line 8, in <module>
    sys.exit(main())
             ^^^^^^
  File "/usr/local/lib/python3.11/site-packages/nautobot/core/cli/__init__.py", line 53, in main
    run_app(
  File "/usr/local/lib/python3.11/site-packages/nautobot/core/runner/runner.py", line 297, in run_app
    management.execute_from_command_line([runner_name, command] + command_args)
  File "/usr/local/lib/python3.11/site-packages/django/core/management/__init__.py", line 419, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python3.11/site-packages/django/core/management/__init__.py", line 413, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python3.11/site-packages/django/core/management/base.py", line 354, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/usr/local/lib/python3.11/site-packages/django/core/management/base.py", line 398, in execute
    output = self.handle(*args, **options)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/source/nautobot_netbox_importer/management/commands/import_netbox.py", line 88, in handle
    adapter.import_to_nautobot()
  File "/source/nautobot_netbox_importer/diffsync/adapters/netbox.py", line 94, in import_to_nautobot
    self._atomic_import()
  File "/usr/local/lib/python3.11/contextlib.py", line 81, in inner
    return func(*args, **kwds)
           ^^^^^^^^^^^^^^^^^^^
  File "/source/nautobot_netbox_importer/diffsync/adapters/netbox.py", line 113, in _atomic_import
    diff = self.nautobot.sync_from(self)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/diffsync/__init__.py", line 573, in sync_from
    result = syncer.perform_sync()
             ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/diffsync/helpers.py", line 329, in perform_sync
    changed |= self.sync_diff_element(element)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/diffsync/helpers.py", line 379, in sync_diff_element
    changed, modified_model = self.sync_model(src_model=src_model, dst_model=dst_model, ids=ids, attrs=attrs)
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/diffsync/helpers.py", line 428, in sync_model
    dst_model = self.model_class.create(diffsync=self.dst_diffsync, ids=ids, attrs=attrs)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/source/nautobot_netbox_importer/generator/nautobot.py", line 523, in create
    cls._wrapper.save_nautobot_instance(instance, attrs)
  File "/source/nautobot_netbox_importer/generator/nautobot.py", line 483, in save_nautobot_instance
    instance.save()
  File "/usr/local/lib/python3.11/site-packages/nautobot/extras/models/customfields.py", line 417, in save
    super().save(*args, **kwargs)
  File "/usr/local/lib/python3.11/site-packages/django/db/models/base.py", line 739, in save
    self.save_base(using=using, force_insert=force_insert,
  File "/usr/local/lib/python3.11/site-packages/django/db/models/base.py", line 776, in save_base
    updated = self._save_table(
              ^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/models/base.py", line 881, in _save_table
    results = self._do_insert(cls._base_manager, using, fields, returning_fields, raw)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/models/base.py", line 919, in _do_insert
    return manager._insert(
           ^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/models/manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/models/query.py", line 1270, in _insert
    return query.get_compiler(using=using).execute_sql(returning_fields)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/models/sql/compiler.py", line 1416, in execute_sql
    cursor.execute(sql, params)
  File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 98, in execute
    return super().execute(sql, params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 66, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 75, in _execute_with_wrappers
    return executor(sql, params, many, context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 79, in _execute
    with self.db.wrap_database_errors:
  File "/usr/local/lib/python3.11/site-packages/django/db/utils.py", line 90, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django_prometheus/db/common.py", line 69, in execute
    return super().execute(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
django.db.utils.IntegrityError: null value in column "label" of relation "extras_customfield" violates not-null constraint
DETAIL:  Failing row contains (30b7f5c2-3ef4-571f-9781-bde928c2ce8b, boolean, null, , t, loose, false, 100, null, null, , 2024-02-21 16:53:16.120802+00, 2024-02-21 16:53:16.12081+00, f, base_playbook_completed, ).

This is the data from the netbox dump:

{
  "model": "extras.customfield",
  "pk": 8,
  "fields": {
    "created": "2022-05-30T00:00:00Z",
    "last_updated": "2023-04-21T16:15:24.630Z",
    "type": "boolean",
    "object_type": null,
    "name": "base_playbook_completed",
    "label": "",
    "group_name": "",
    "description": "",
    "required": true,
    "search_weight": 1000,
    "filter_logic": "loose",
    "default": 0,
    "weight": 100,
    "validation_minimum": null,
    "validation_maximum": null,
    "validation_regex": "",
    "choice_set": null,
    "ui_visible": "always",
    "ui_editable": "yes",
    "is_cloneable": false,
    "content_types": [
      [
        "dcim",
        "device"
      ],
      [
        "virtualization",
        "virtualmachine"
      ]
    ]
  }
}

@snaselj
Copy link
Contributor Author

snaselj commented Feb 22, 2024

Thanks for the fix, it worked for that issue, now I'm facing an issue with extras.customfields with empty labels ("label": ""):

Thanks for the detailed report @helderbetiol , fixed.

@helderbetiol
Copy link

Hi @snaselj ,

I'm having a new issue:

2024-02-22 10:11:00 [debug    ] Attempting model create        action=create diffs={'+': {'content_types': {5}, 'name': 'Network', 'color': '00ff00', 'created': datetime.datetime(2020, 5, 28, 0, 0, tzinfo=datetime.timezone.utc), 'last_updated': None, 'custom_field_data': None, 'description': 'Dedicated to network', 'weight': None}} dst=<NautobotAdapter "Nautobot"> flags=<DiffSyncFlags.NONE: 0> model=extras_role src=<NetBoxAdapter "NetBox"> unique_id=cdec8e7a-3a27-5c46-b9a3-06c5b38d7b1e
10:11:00.061 ERROR   nautobot-netbox-importer nautobot.py             save_nautobot_instance() :
  Save failed: <class 'nautobot.extras.models.roles.Role'> {'_state': <django.db.models.base.ModelState object at 0xffff76121250>, 'id': UUID('cdec8e7a-3a27-5c46-b9a3-06c5b38d7b1e'), 'created': datetime.datetime(2024, 2, 22, 10, 11, 0, 56575, tzinfo=<UTC>), 'last_updated': datetime.datetime(2024, 2, 22, 10, 11, 0, 56581, tzinfo=<UTC>), '_custom_field_data': {}, 'name': 'Network', 'color': '00ff00', 'description': 'Dedicated to network', 'weight': None}
Save failed: <class 'nautobot.extras.models.roles.Role'> {'_state': <django.db.models.base.ModelState object at 0xffff76121250>, 'id': UUID('cdec8e7a-3a27-5c46-b9a3-06c5b38d7b1e'), 'created': datetime.datetime(2024, 2, 22, 10, 11, 0, 56575, tzinfo=<UTC>), 'last_updated': datetime.datetime(2024, 2, 22, 10, 11, 0, 56581, tzinfo=<UTC>), '_custom_field_data': {}, 'name': 'Network', 'color': '00ff00', 'description': 'Dedicated to network', 'weight': None}
Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django_prometheus/db/common.py", line 69, in execute
    return super().execute(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
psycopg2.errors.UniqueViolation: duplicate key value violates unique constraint "extras_role_name_key"
DETAIL:  Key (name)=(Network) already exists.


The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/local/bin/nautobot-server", line 8, in <module>
    sys.exit(main())
             ^^^^^^
  File "/usr/local/lib/python3.11/site-packages/nautobot/core/cli/__init__.py", line 53, in main
    run_app(
  File "/usr/local/lib/python3.11/site-packages/nautobot/core/runner/runner.py", line 297, in run_app
    management.execute_from_command_line([runner_name, command] + command_args)
  File "/usr/local/lib/python3.11/site-packages/django/core/management/__init__.py", line 419, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python3.11/site-packages/django/core/management/__init__.py", line 413, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python3.11/site-packages/django/core/management/base.py", line 354, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/usr/local/lib/python3.11/site-packages/django/core/management/base.py", line 398, in execute
    output = self.handle(*args, **options)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/source/nautobot_netbox_importer/management/commands/import_netbox.py", line 88, in handle
    adapter.import_to_nautobot()
  File "/source/nautobot_netbox_importer/diffsync/adapters/netbox.py", line 94, in import_to_nautobot
    self._atomic_import()
  File "/usr/local/lib/python3.11/contextlib.py", line 81, in inner
    return func(*args, **kwds)
           ^^^^^^^^^^^^^^^^^^^
  File "/source/nautobot_netbox_importer/diffsync/adapters/netbox.py", line 113, in _atomic_import
    diff = self.nautobot.sync_from(self)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/diffsync/__init__.py", line 573, in sync_from
    result = syncer.perform_sync()
             ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/diffsync/helpers.py", line 329, in perform_sync
    changed |= self.sync_diff_element(element)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/diffsync/helpers.py", line 379, in sync_diff_element
    changed, modified_model = self.sync_model(src_model=src_model, dst_model=dst_model, ids=ids, attrs=attrs)
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/diffsync/helpers.py", line 428, in sync_model
    dst_model = self.model_class.create(diffsync=self.dst_diffsync, ids=ids, attrs=attrs)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/source/nautobot_netbox_importer/generator/nautobot.py", line 523, in create
    cls._wrapper.save_nautobot_instance(instance, attrs)
  File "/source/nautobot_netbox_importer/generator/nautobot.py", line 483, in save_nautobot_instance
    instance.save()
  File "/usr/local/lib/python3.11/site-packages/django/db/models/base.py", line 739, in save
    self.save_base(using=using, force_insert=force_insert,
  File "/usr/local/lib/python3.11/site-packages/django/db/models/base.py", line 776, in save_base
    updated = self._save_table(
              ^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/models/base.py", line 881, in _save_table
    results = self._do_insert(cls._base_manager, using, fields, returning_fields, raw)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/models/base.py", line 919, in _do_insert
    return manager._insert(
           ^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/models/manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/models/query.py", line 1270, in _insert
    return query.get_compiler(using=using).execute_sql(returning_fields)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/models/sql/compiler.py", line 1416, in execute_sql
    cursor.execute(sql, params)
  File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 98, in execute
    return super().execute(sql, params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 66, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 75, in _execute_with_wrappers
    return executor(sql, params, many, context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 79, in _execute
    with self.db.wrap_database_errors:
  File "/usr/local/lib/python3.11/site-packages/django/db/utils.py", line 90, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django_prometheus/db/common.py", line 69, in execute
    return super().execute(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
django.db.utils.IntegrityError: duplicate key value violates unique constraint "extras_role_name_key"
DETAIL:  Key (name)=(Network) already exists.

In our netbox dump, I see that this is what we are trying to create here:

{
  "model": "dcim.rackrole",
  "pk": 1,
  "fields": {
    "created": "2020-05-28T00:00:00Z",
    "last_updated": "2020-05-28T08:28:04.662Z",
    "custom_field_data": {},
    "name": "Network",
    "slug": "network",
    "description": "Dedicated to network",
    "color": "00ff00"
  }
}

We also have a dcim.devicerole with the same name in the dump, I suppose this is why we have this issue:

{
  "model": "dcim.devicerole",
  "pk": 2,
  "fields": {
    "created": "2020-05-26T00:00:00Z",
    "last_updated": "2020-05-28T09:09:08.533Z",
    "custom_field_data": {},
    "name": "Network",
    "slug": "network",
    "description": "",
    "color": "2196f3",
    "vm_role": false,
    "config_template": null
  }
}

I can see that last one being successfully created in the logs prior to the error:

2024-02-22 10:10:59 [debug    ] Attempting model create        action=create diffs={'+': {'content_types': {3}, 'name': 'Network', 'color': '2196f3', 'created': datetime.datetime(2020, 5, 26, 0, 0, tzinfo=datetime.timezone.utc), 'last_updated': None, 'custom_field_data': None, 'description': None, 'weight': None}} dst=<NautobotAdapter "Nautobot"> flags=<DiffSyncFlags.NONE: 0> model=extras_role src=<NetBoxAdapter "NetBox"> unique_id=8fe32a3e-1fd0-5e31-8435-20be4a80668c
2024-02-22 10:10:59 [info     ] Created successfully           action=create diffs={'+': {'content_types': {3}, 'name': 'Network', 'color': '2196f3', 'created': datetime.datetime(2020, 5, 26, 0, 0, tzinfo=datetime.timezone.utc), 'last_updated': None, 'custom_field_data': None, 'description': None, 'weight': None}} dst=<NautobotAdapter "Nautobot"> flags=<DiffSyncFlags.NONE: 0> model=extras_role src=<NetBoxAdapter "NetBox"> status=success unique_id=8fe32a3e-1fd0-5e31-8435-20be4a80668c

Is this something that could be fixed? Or maybe I can try to change the name of one of them.

@snaselj
Copy link
Contributor Author

snaselj commented Feb 22, 2024

Hi @snaselj ,

I'm having a new issue:

Is this something that could be fixed? Or maybe I can try to change the name of one of them.

Will fix it and let you know @helderbetiol .

@snaselj
Copy link
Contributor Author

snaselj commented Feb 22, 2024

Hi @snaselj ,
I'm having a new issue:
Is this something that could be fixed? Or maybe I can try to change the name of one of them.

Will fix it and let you know @helderbetiol .

Hi @helderbetiol ,

The issue with duplicit role names is fixed now, thanks for your reports.

@snaselj snaselj force-pushed the u/snaselj-napps-204-netbox-v3.7 branch from 80d9d12 to 220646e Compare February 28, 2024 07:17
@snaselj snaselj marked this pull request as ready for review February 28, 2024 07:39
@snaselj
Copy link
Contributor Author

snaselj commented Feb 28, 2024

Cleaned up this pull request to contain only changes related to NetBox 3.7 @glennmatthews @chadell

@helderbetiol
Copy link

Hi @snaselj ,
I'm having a new issue:
Is this something that could be fixed? Or maybe I can try to change the name of one of them.

Will fix it and let you know @helderbetiol .

Hi @helderbetiol ,

The issue with duplicit role names is fixed now, thanks for your reports.

Hi @snaselj ,

I got a new issue, could you please check if it can be fixed?

2024-02-28 09:42:38 [debug    ] Attempting model create        action=create diffs={'+': {'status_id': UUID('318a7220-4327-48ca-876c-d1389a81758c'), 'created': datetime.datetime(2024, 1, 5, 17, 34, 10, 565000, tzinfo=datetime.timezone.utc), 'last_updated': None, 'custom_field_data': None, 'type': 'power', 'label': None, 'color': 'ffeb3b', 'length': 1, 'length_unit': 'm', 'termination_a_type_id': None, 'termination_a_id': None, 'termination_b_type_id': 63, 'termination_b_id': UUID('6c0b9a2c-2145-55a3-b6d9-df376949fabb')}} dst=<NautobotAdapter "Nautobot"> flags=<DiffSyncFlags.NONE: 0> model=dcim_cable src=<NetBoxAdapter "NetBox"> unique_id=4c63852f-7cfd-5b2d-94a3-47d0c31544e7
09:42:38.116 ERROR   nautobot-netbox-importer nautobot.py             save_nautobot_instance() :
  Save failed: <class 'nautobot.dcim.models.cables.Cable'> {'_state': <django.db.models.base.ModelState object at 0xffff54f25e50>, 'id': UUID('4c63852f-7cfd-5b2d-94a3-47d0c31544e7'), 'created': datetime.datetime(2024, 2, 28, 9, 42, 38, 81322, tzinfo=<UTC>), 'last_updated': datetime.datetime(2024, 2, 28, 9, 42, 38, 81332, tzinfo=<UTC>), '_custom_field_data': {}, 'termination_a_type_id': None, 'termination_a_id': None, 'termination_b_type_id': 63, 'termination_b_id': UUID('6c0b9a2c-2145-55a3-b6d9-df376949fabb'), 'type': 'power', 'status_id': UUID('318a7220-4327-48ca-876c-d1389a81758c'), 'label': '', 'color': 'ffeb3b', 'length': 1, 'length_unit': 'm', '_abs_length': 1, '_termination_a_device_id': None, '_termination_b_device_id': UUID('c30ef835-0092-57db-a0b2-9600e09fdf97'), '_pk': UUID('4c63852f-7cfd-5b2d-94a3-47d0c31544e7'), '_orig_status': None}
Save failed: <class 'nautobot.dcim.models.cables.Cable'> {'_state': <django.db.models.base.ModelState object at 0xffff54f25e50>, 'id': UUID('4c63852f-7cfd-5b2d-94a3-47d0c31544e7'), 'created': datetime.datetime(2024, 2, 28, 9, 42, 38, 81322, tzinfo=<UTC>), 'last_updated': datetime.datetime(2024, 2, 28, 9, 42, 38, 81332, tzinfo=<UTC>), '_custom_field_data': {}, 'termination_a_type_id': None, 'termination_a_id': None, 'termination_b_type_id': 63, 'termination_b_id': UUID('6c0b9a2c-2145-55a3-b6d9-df376949fabb'), 'type': 'power', 'status_id': UUID('318a7220-4327-48ca-876c-d1389a81758c'), 'label': '', 'color': 'ffeb3b', 'length': 1, 'length_unit': 'm', '_abs_length': 1, '_termination_a_device_id': None, '_termination_b_device_id': UUID('c30ef835-0092-57db-a0b2-9600e09fdf97'), '_pk': UUID('4c63852f-7cfd-5b2d-94a3-47d0c31544e7'), '_orig_status': None}
Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django_prometheus/db/common.py", line 69, in execute
    return super().execute(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
psycopg2.errors.NotNullViolation: null value in column "termination_a_id" of relation "dcim_cable" violates not-null constraint
DETAIL:  Failing row contains (4c63852f-7cfd-5b2d-94a3-47d0c31544e7, 2024-02-28 09:42:38.081322+00, 2024-02-28 09:42:38.081332+00, {}, null, 6c0b9a2c-2145-55a3-b6d9-df376949fabb, power, , ffeb3b, 1, m, 1.0000, null, c30ef835-0092-57db-a0b2-9600e09fdf97, 318a7220-4327-48ca-876c-d1389a81758c, null, 63).


The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/local/bin/nautobot-server", line 8, in <module>
    sys.exit(main())
             ^^^^^^
  File "/usr/local/lib/python3.11/site-packages/nautobot/core/cli/__init__.py", line 53, in main
    run_app(
  File "/usr/local/lib/python3.11/site-packages/nautobot/core/runner/runner.py", line 297, in run_app
    management.execute_from_command_line([runner_name, command] + command_args)
  File "/usr/local/lib/python3.11/site-packages/django/core/management/__init__.py", line 419, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python3.11/site-packages/django/core/management/__init__.py", line 413, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python3.11/site-packages/django/core/management/base.py", line 354, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/usr/local/lib/python3.11/site-packages/django/core/management/base.py", line 398, in execute
    output = self.handle(*args, **options)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/source/nautobot_netbox_importer/management/commands/import_netbox.py", line 95, in handle
    adapter.import_to_nautobot()
  File "/source/nautobot_netbox_importer/diffsync/adapters/netbox.py", line 98, in import_to_nautobot
    self._atomic_import()
  File "/usr/local/lib/python3.11/contextlib.py", line 81, in inner
    return func(*args, **kwds)
           ^^^^^^^^^^^^^^^^^^^
  File "/source/nautobot_netbox_importer/diffsync/adapters/netbox.py", line 117, in _atomic_import
    diff = self.nautobot.sync_from(self)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/diffsync/__init__.py", line 573, in sync_from
    result = syncer.perform_sync()
             ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/diffsync/helpers.py", line 329, in perform_sync
    changed |= self.sync_diff_element(element)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/diffsync/helpers.py", line 379, in sync_diff_element
    changed, modified_model = self.sync_model(src_model=src_model, dst_model=dst_model, ids=ids, attrs=attrs)
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/diffsync/helpers.py", line 428, in sync_model
    dst_model = self.model_class.create(diffsync=self.dst_diffsync, ids=ids, attrs=attrs)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/source/nautobot_netbox_importer/generator/nautobot.py", line 559, in create
    cls._wrapper.save_nautobot_instance(instance, attrs)
  File "/source/nautobot_netbox_importer/generator/nautobot.py", line 519, in save_nautobot_instance
    instance.save()
  File "/usr/local/lib/python3.11/site-packages/nautobot/dcim/models/cables.py", line 260, in save
    super().save(*args, **kwargs)
  File "/usr/local/lib/python3.11/site-packages/django/db/models/base.py", line 739, in save
    self.save_base(using=using, force_insert=force_insert,
  File "/usr/local/lib/python3.11/site-packages/django/db/models/base.py", line 776, in save_base
    updated = self._save_table(
              ^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/models/base.py", line 881, in _save_table
    results = self._do_insert(cls._base_manager, using, fields, returning_fields, raw)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/models/base.py", line 919, in _do_insert
    return manager._insert(
           ^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/models/manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/models/query.py", line 1270, in _insert
    return query.get_compiler(using=using).execute_sql(returning_fields)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/models/sql/compiler.py", line 1416, in execute_sql
    cursor.execute(sql, params)
  File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 98, in execute
    return super().execute(sql, params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 66, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 75, in _execute_with_wrappers
    return executor(sql, params, many, context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 79, in _execute
    with self.db.wrap_database_errors:
  File "/usr/local/lib/python3.11/site-packages/django/db/utils.py", line 90, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django_prometheus/db/common.py", line 69, in execute
    return super().execute(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
django.db.utils.IntegrityError: null value in column "termination_a_id" of relation "dcim_cable" violates not-null constraint
DETAIL:  Failing row contains (4c63852f-7cfd-5b2d-94a3-47d0c31544e7, 2024-02-28 09:42:38.081322+00, 2024-02-28 09:42:38.081332+00, {}, null, 6c0b9a2c-2145-55a3-b6d9-df376949fabb, power, , ffeb3b, 1, m, 1.0000, null, c30ef835-0092-57db-a0b2-9600e09fdf97, 318a7220-4327-48ca-876c-d1389a81758c, null, 63).

Comment on lines 65 to 72
--indent=2 \
--natural-primary \
--natural-foreign \
--exclude extras.ObjectChange \
--exclude=extras.ObjectChange \
--exclude=extras.Report \
--exclude=extras.Script \
--exclude=django_rq.Queue \
--output=/tmp/netbox_data.json
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the list of --exclude options need to differ by NetBox version? Or do the same options apply to all supported versions at this time?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it differ. Prior to NetBox 3.7, it was possible to export everything. With NetBox 3.7 I was facing issues dumping mentioned models, so that's why I added these excludes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so do we need to document different instructions for different NetBox versions?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is the following sentence already:

It's possible to exclude other models from the export, but the command provided above is a good starting point.

Not sure whether adding multiple examples for multiple NetBox versions wouldn't make it less readable. I would rather keep it as is and let the user figure it out based on their version and preference. WDYT?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How should the user figure it out? Do we get clear messages if they exclude the wrong models or fail to exclude the correct models?

Copy link
Contributor Author

@snaselj snaselj Feb 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverted --excludes. Added an error message example and how to handle that.

@snaselj
Copy link
Contributor Author

snaselj commented Feb 28, 2024

Hi @snaselj ,

I got a new issue, could you please check if it can be fixed?

2024-02-28 09:42:38 [debug    ] Attempting model create        action=create diffs={'+': {'status_id': UUID('318a7220-4327-48ca-876c-d1389a81758c'), 'created': datetime.datetime(2024, 1, 5, 17, 34, 10, 565000, tzinfo=datetime.timezone.utc), 'last_updated': None, 'custom_field_data': None, 'type': 'power', 'label': None, 'color': 'ffeb3b', 'length': 1, 'length_unit': 'm', 'termination_a_type_id': None, 'termination_a_id': None, 'termination_b_type_id': 63, 'termination_b_id': UUID('6c0b9a2c-2145-55a3-b6d9-df376949fabb')}} dst=<NautobotAdapter "Nautobot"> flags=<DiffSyncFlags.NONE: 0> model=dcim_cable src=<NetBoxAdapter "NetBox"> unique_id=4c63852f-7cfd-5b2d-94a3-47d0c31544e7
09:42:38.116 ERROR   nautobot-netbox-importer nautobot.py             save_nautobot_instance() :
  Save failed: <class 'nautobot.dcim.models.cables.Cable'> {'_state': <django.db.models.base.ModelState object at 0xffff54f25e50>, 'id': UUID('4c63852f-7cfd-5b2d-94a3-47d0c31544e7'), 'created': datetime.datetime(2024, 2, 28, 9, 42, 38, 81322, tzinfo=<UTC>), 'last_updated': datetime.datetime(2024, 2, 28, 9, 42, 38, 81332, tzinfo=<UTC>), '_custom_field_data': {}, 'termination_a_type_id': None, 'termination_a_id': None, 'termination_b_type_id': 63, 'termination_b_id': UUID('6c0b9a2c-2145-55a3-b6d9-df376949fabb'), 'type': 'power', 'status_id': UUID('318a7220-4327-48ca-876c-d1389a81758c'), 'label': '', 'color': 'ffeb3b', 'length': 1, 'length_unit': 'm', '_abs_length': 1, '_termination_a_device_id': None, '_termination_b_device_id': UUID('c30ef835-0092-57db-a0b2-9600e09fdf97'), '_pk': UUID('4c63852f-7cfd-5b2d-94a3-47d0c31544e7'), '_orig_status': None}
django.db.utils.IntegrityError: null value in column "termination_a_id" of relation "dcim_cable" violates not-null constraint
DETAIL:  Failing row contains (4c63852f-7cfd-5b2d-94a3-47d0c31544e7, 2024-02-28 09:42:38.081322+00, 2024-02-28 09:42:38.081332+00, {}, null, 6c0b9a2c-2145-55a3-b6d9-df376949fabb, power, , ffeb3b, 1, m, 1.0000, null, c30ef835-0092-57db-a0b2-9600e09fdf97, 318a7220-4327-48ca-876c-d1389a81758c, null, 63).

Hi @helderbetiol ,

Not sure, how to fix this. I just added a workaround, to collect and skip Nautobot instances that fails instance.save() call. Tested it works, however there could be issues with other DB constraints, when these skipped instances will be referenced. Let me know pls. if it works for you, or is there any other issue.

The order of fields in identifiers matters, as those are used to
lookups.
@snaselj snaselj merged commit 5927d74 into develop Feb 29, 2024
16 checks passed
@snaselj snaselj deleted the u/snaselj-napps-204-netbox-v3.7 branch February 29, 2024 14:38
@snaselj
Copy link
Contributor Author

snaselj commented Feb 29, 2024

Hi @snaselj ,
I got a new issue, could you please check if it can be fixed?

2024-02-28 09:42:38 [debug    ] Attempting model create        action=create diffs={'+': {'status_id': UUID('318a7220-4327-48ca-876c-d1389a81758c'), 'created': datetime.datetime(2024, 1, 5, 17, 34, 10, 565000, tzinfo=datetime.timezone.utc), 'last_updated': None, 'custom_field_data': None, 'type': 'power', 'label': None, 'color': 'ffeb3b', 'length': 1, 'length_unit': 'm', 'termination_a_type_id': None, 'termination_a_id': None, 'termination_b_type_id': 63, 'termination_b_id': UUID('6c0b9a2c-2145-55a3-b6d9-df376949fabb')}} dst=<NautobotAdapter "Nautobot"> flags=<DiffSyncFlags.NONE: 0> model=dcim_cable src=<NetBoxAdapter "NetBox"> unique_id=4c63852f-7cfd-5b2d-94a3-47d0c31544e7
09:42:38.116 ERROR   nautobot-netbox-importer nautobot.py             save_nautobot_instance() :
  Save failed: <class 'nautobot.dcim.models.cables.Cable'> {'_state': <django.db.models.base.ModelState object at 0xffff54f25e50>, 'id': UUID('4c63852f-7cfd-5b2d-94a3-47d0c31544e7'), 'created': datetime.datetime(2024, 2, 28, 9, 42, 38, 81322, tzinfo=<UTC>), 'last_updated': datetime.datetime(2024, 2, 28, 9, 42, 38, 81332, tzinfo=<UTC>), '_custom_field_data': {}, 'termination_a_type_id': None, 'termination_a_id': None, 'termination_b_type_id': 63, 'termination_b_id': UUID('6c0b9a2c-2145-55a3-b6d9-df376949fabb'), 'type': 'power', 'status_id': UUID('318a7220-4327-48ca-876c-d1389a81758c'), 'label': '', 'color': 'ffeb3b', 'length': 1, 'length_unit': 'm', '_abs_length': 1, '_termination_a_device_id': None, '_termination_b_device_id': UUID('c30ef835-0092-57db-a0b2-9600e09fdf97'), '_pk': UUID('4c63852f-7cfd-5b2d-94a3-47d0c31544e7'), '_orig_status': None}
django.db.utils.IntegrityError: null value in column "termination_a_id" of relation "dcim_cable" violates not-null constraint
DETAIL:  Failing row contains (4c63852f-7cfd-5b2d-94a3-47d0c31544e7, 2024-02-28 09:42:38.081322+00, 2024-02-28 09:42:38.081332+00, {}, null, 6c0b9a2c-2145-55a3-b6d9-df376949fabb, power, , ffeb3b, 1, m, 1.0000, null, c30ef835-0092-57db-a0b2-9600e09fdf97, 318a7220-4327-48ca-876c-d1389a81758c, null, 63).

Hi @helderbetiol ,

Not sure, how to fix this. I just added a workaround, to collect and skip Nautobot instances that fails instance.save() call. Tested it works, however there could be issues with other DB constraints, when these skipped instances will be referenced. Let me know pls. if it works for you, or is there any other issue.

Hi @helderbetiol, @hchybz

I've merged this pull request. If you have any issues, don't hesitate to open an issue in this repository and mark me there, thanks.

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

Successfully merging this pull request may close these issues.

4 participants