From a9bb29a247c513cb171a0b7375170d2ec05d62a5 Mon Sep 17 00:00:00 2001 From: Patrick Avery Date: Fri, 11 Sep 2020 23:21:21 -0700 Subject: [PATCH 1/5] implements support for virtual-machines in NetBoxInventory2 --- nornir_netbox/plugins/inventory/netbox.py | 45 +- .../NetBoxInventory2/2.8.9/vms-expected.json | 610 ++++++++++++++++++ tests/mocked/2.8.9/virtual-machines-0.json | 72 +++ tests/mocked/2.8.9/virtual-machines-1.json | 66 ++ tests/mocked/2.8.9/virtual-machines-2.json | 134 ++++ tests/mocked/2.8.9/virtual-machines.json | 258 ++++++++ tests/test_netbox.py | 46 +- 7 files changed, 1213 insertions(+), 18 deletions(-) create mode 100644 tests/NetBoxInventory2/2.8.9/vms-expected.json create mode 100644 tests/mocked/2.8.9/virtual-machines-0.json create mode 100644 tests/mocked/2.8.9/virtual-machines-1.json create mode 100644 tests/mocked/2.8.9/virtual-machines-2.json create mode 100644 tests/mocked/2.8.9/virtual-machines.json diff --git a/nornir_netbox/plugins/inventory/netbox.py b/nornir_netbox/plugins/inventory/netbox.py index 6e3174d..941b074 100644 --- a/nornir_netbox/plugins/inventory/netbox.py +++ b/nornir_netbox/plugins/inventory/netbox.py @@ -183,6 +183,9 @@ class NetBoxInventory2: flatten_custom_fields: Assign custom fields directly to the host's data attribute (defaults to False) filter_parameters: Key-value pairs that allow you to filter the NetBox inventory. + include_vms: Get virtual machines from NetBox as well as devices. + (defaults to False) + """ def __init__( @@ -192,6 +195,7 @@ def __init__( ssl_verify: Union[bool, str] = True, flatten_custom_fields: bool = False, filter_parameters: Optional[Dict[str, Any]] = None, + include_vms: bool = False, **kwargs: Any, ) -> None: filter_parameters = filter_parameters or {} @@ -203,6 +207,7 @@ def __init__( self.nb_url = nb_url self.flatten_custom_fields = flatten_custom_fields self.filter_parameters = filter_parameters + self.include_vms = include_vms self.session = requests.Session() self.session.headers.update({"Authorization": f"Token {nb_token}"}) @@ -210,21 +215,20 @@ def __init__( def load(self) -> Inventory: - url = f"{self.nb_url}/api/dcim/devices/?limit=0" nb_devices: List[Dict[str, Any]] = [] - while url: - r = self.session.get(url, params=self.filter_parameters) + nb_devices = self._get_resources( + url=f"{self.nb_url}/api/dcim/devices/?limit=0", + params=self.filter_parameters, + ) - if not r.status_code == 200: - raise ValueError( - f"Failed to get devices from NetBox instance {self.nb_url}" + if self.include_vms: + nb_devices.extend( + self._get_resources( + url=f"{self.nb_url}/api/virtualization/virtual-machines/?limit=0", + params=self.filter_parameters, ) - - resp = r.json() - nb_devices.extend(resp.get("results")) - - url = resp.get("next") + ) hosts = Hosts() groups = Groups() @@ -263,3 +267,22 @@ def load(self) -> Inventory: ) return Inventory(hosts=hosts, groups=groups, defaults=defaults) + + def _get_resources(self, url: str, params: Dict[str, Any]) -> List[Dict[str, Any]]: + + resources: List[Dict[str, Any]] = [] + + while url: + r = self.session.get(url, params=params) + + if not r.status_code == 200: + raise ValueError( + f"Failed to get data from NetBox instance {self.nb_url}" + ) + + resp = r.json() + resources.extend(resp.get("results")) + + url = resp.get("next") + + return resources diff --git a/tests/NetBoxInventory2/2.8.9/vms-expected.json b/tests/NetBoxInventory2/2.8.9/vms-expected.json new file mode 100644 index 0000000..6d34d39 --- /dev/null +++ b/tests/NetBoxInventory2/2.8.9/vms-expected.json @@ -0,0 +1,610 @@ +{ + "hosts": { + "4": { + "name": "4", + "connection_options": {}, + "groups": [], + "data": { + "id": 4, + "name": null, + "display_name": "MX480", + "device_type": { + "id": 11, + "url": "http://localhost:8080/api/dcim/device-types/11/", + "manufacturer": { + "id": 3, + "url": "http://localhost:8080/api/dcim/manufacturers/3/", + "name": "Juniper", + "slug": "juniper" + }, + "model": "MX480", + "slug": "mx480" + }, + "device_role": { + "id": 1, + "url": "http://localhost:8080/api/dcim/device-roles/1/", + "name": "Router", + "slug": "rt" + }, + "tenant": null, + "platform": { + "id": 1, + "url": "http://localhost:8080/api/dcim/platforms/1/", + "name": "junos", + "slug": "junos" + }, + "serial": "", + "asset_tag": null, + "site": { + "id": 3, + "url": "http://localhost:8080/api/dcim/sites/3/", + "name": "Sunnyvale, CA", + "slug": "sunnyvale-ca" + }, + "rack": null, + "position": null, + "face": null, + "parent_device": null, + "status": { + "value": 1, + "label": "Active" + }, + "primary_ip": { + "id": 4, + "url": "http://localhost:8080/api/ipam/ip-addresses/4/", + "family": 4, + "address": "10.0.1.4/32" + }, + "primary_ip4": { + "id": 1, + "url": "http://localhost:8080/api/ipam/ip-addresses/4/", + "family": 4, + "address": "10.0.1.4/32" + }, + "primary_ip6": null, + "cluster": null, + "virtual_chassis": null, + "vc_position": null, + "vc_priority": null, + "comments": "", + "custom_fields": {}, + "created": "2018-07-12", + "last_updated": "2018-07-12T11:53:54.742412Z" + }, + "hostname": "10.0.1.4", + "port": null, + "username": null, + "password": null, + "platform": "junos" + }, + "1-Core": { + "name": "1-Core", + "connection_options": {}, + "groups": [], + "data": { + "id": 1, + "name": "1-Core", + "display_name": "1-Core", + "device_type": { + "id": 11, + "url": "http://localhost:8080/api/dcim/device-types/11/", + "manufacturer": { + "id": 3, + "url": "http://localhost:8080/api/dcim/manufacturers/3/", + "name": "Juniper", + "slug": "juniper" + }, + "model": "MX480", + "slug": "mx480" + }, + "device_role": { + "id": 1, + "url": "http://localhost:8080/api/dcim/device-roles/1/", + "name": "Router", + "slug": "rt" + }, + "tenant": null, + "platform": { + "id": 1, + "url": "http://localhost:8080/api/dcim/platforms/1/", + "name": "junos", + "slug": "junos" + }, + "serial": "", + "asset_tag": null, + "site": { + "id": 3, + "url": "http://localhost:8080/api/dcim/sites/3/", + "name": "Sunnyvale, CA", + "slug": "sunnyvale-ca" + }, + "rack": null, + "position": null, + "face": null, + "parent_device": null, + "status": { + "value": 1, + "label": "Active" + }, + "primary_ip": { + "id": 1, + "url": "http://localhost:8080/api/ipam/ip-addresses/1/", + "family": 4, + "address": "10.0.1.1/32" + }, + "primary_ip4": { + "id": 1, + "url": "http://localhost:8080/api/ipam/ip-addresses/1/", + "family": 4, + "address": "10.0.1.1/32" + }, + "primary_ip6": null, + "cluster": null, + "virtual_chassis": null, + "vc_position": null, + "vc_priority": null, + "comments": "", + "custom_fields": {}, + "created": "2018-07-12", + "last_updated": "2018-07-12T11:53:54.742412Z" + }, + "hostname": "10.0.1.1", + "port": null, + "username": null, + "password": null, + "platform": "junos" + }, + "2-Distribution": { + "name": "2-Distribution", + "connection_options": {}, + "groups": [], + "data": { + "id": 2, + "name": "2-Distribution", + "display_name": "2-Distribution", + "device_type": { + "id": 9, + "url": "http://localhost:8080/api/dcim/device-types/9/", + "manufacturer": { + "id": 3, + "url": "http://localhost:8080/api/dcim/manufacturers/3/", + "name": "Juniper", + "slug": "juniper" + }, + "model": "EX4550-32F", + "slug": "ex4550-32f" + }, + "device_role": { + "id": 1, + "url": "http://localhost:8080/api/dcim/device-roles/1/", + "name": "Router", + "slug": "rt" + }, + "tenant": null, + "platform": null, + "serial": "", + "asset_tag": null, + "site": { + "id": 3, + "url": "http://localhost:8080/api/dcim/sites/3/", + "name": "Sunnyvale, CA", + "slug": "sunnyvale-ca" + }, + "rack": null, + "position": null, + "face": null, + "parent_device": null, + "status": { + "value": 1, + "label": "Active" + }, + "primary_ip": { + "id": 2, + "url": "http://localhost:8080/api/ipam/ip-addresses/2/", + "family": 4, + "address": "172.16.2.1/32" + }, + "primary_ip4": { + "id": 2, + "url": "http://localhost:8080/api/ipam/ip-addresses/2/", + "family": 4, + "address": "172.16.2.1/32" + }, + "primary_ip6": null, + "cluster": null, + "virtual_chassis": null, + "vc_position": null, + "vc_priority": null, + "comments": "", + "custom_fields": {}, + "created": "2018-07-12", + "last_updated": "2018-07-12T11:53:54.802934Z" + }, + "hostname": "172.16.2.1", + "port": null, + "username": null, + "password": null, + "platform": null + }, + "3-Access": { + "name": "3-Access", + "connection_options": {}, + "groups": [], + "data": { + "id": 3, + "name": "3-Access", + "display_name": "3-Access", + "device_type": { + "id": 2, + "url": "http://localhost:8080/api/dcim/device-types/2/", + "manufacturer": { + "id": 2, + "url": "http://localhost:8080/api/dcim/manufacturers/2/", + "name": "Cisco", + "slug": "cisco" + }, + "model": "3650-48TQ-L", + "slug": "3650-48tq-l" + }, + "device_role": { + "id": 2, + "url": "http://localhost:8080/api/dcim/device-roles/2/", + "name": "Switch", + "slug": "sw" + }, + "tenant": null, + "platform": { + "id": 2, + "url": "http://localhost:8080/api/dcim/platforms/2/", + "name": "ios", + "slug": "ios" + }, + "serial": "", + "asset_tag": null, + "site": { + "id": 2, + "url": "http://localhost:8080/api/dcim/sites/2/", + "name": "San Jose, CA", + "slug": "san-jose-ca" + }, + "rack": null, + "position": null, + "face": null, + "parent_device": null, + "status": { + "value": 1, + "label": "Active" + }, + "primary_ip": { + "id": 3, + "url": "http://localhost:8080/api/ipam/ip-addresses/3/", + "family": 4, + "address": "192.168.3.1/32" + }, + "primary_ip4": { + "id": 3, + "url": "http://localhost:8080/api/ipam/ip-addresses/3/", + "family": 4, + "address": "192.168.3.1/32" + }, + "primary_ip6": null, + "cluster": null, + "virtual_chassis": null, + "vc_position": null, + "vc_priority": null, + "comments": "", + "custom_fields": { + "user_defined": 1 + }, + "created": "2018-07-12", + "last_updated": "2018-07-12T11:53:54.866133Z" + }, + "hostname": "192.168.3.1", + "port": null, + "username": null, + "password": null, + "platform": "ios" + }, + "ca-expedition-10": { + "name": "ca-expedition-10", + "connection_options": {}, + "groups": [], + "data": { + "id": 4, + "name": "ca-expedition-10", + "status": { + "value": "active", + "label": "Active", + "id": 1 + }, + "site": null, + "cluster": { + "id": 3, + "url": "http://localhost:8000/api/virtualization/clusters/3/", + "name": "cluster-abc" + }, + "role": { + "id": 4, + "url": "http://localhost:8000/api/dcim/device-roles/4/", + "name": "server", + "slug": "server" + }, + "tenant": null, + "platform": { + "id": 8, + "url": "http://localhost:8000/api/dcim/platforms/8/", + "name": "Expedition", + "slug": "expedition" + }, + "primary_ip": { + "id": 6, + "url": "http://localhost:8000/api/ipam/ip-addresses/6/", + "family": 4, + "address": "2.2.2.2/24" + }, + "primary_ip4": { + "id": 6, + "url": "http://localhost:8000/api/ipam/ip-addresses/6/", + "family": 4, + "address": "2.2.2.2/24" + }, + "primary_ip6": null, + "vcpus": 12, + "memory": 16000, + "disk": 1000, + "comments": "", + "local_context_data": { + "transition_groups": [ + "ASA-5525", + "ASA-5550", + "ASA-5550-Internet" + ] + }, + "tags": [ + "temporary" + ], + "custom_fields": {}, + "config_context": { + "transition_groups": [ + "ASA-5525", + "ASA-5550", + "ASA-5550-Internet" + ] + }, + "created": "2020-09-06", + "last_updated": "2020-09-06T05:03:17.932973Z" + }, + "hostname": "2.2.2.2", + "port": null, + "username": null, + "password": null, + "platform": "Expedition" + }, + "ca-panorama-1": { + "name": "ca-panorama-1", + "connection_options": {}, + "groups": [], + "data": { + "id": 2, + "name": "ca-panorama-1", + "status": { + "value": "active", + "label": "Active", + "id": 1 + }, + "site": null, + "cluster": { + "id": 3, + "url": "http://localhost:8000/api/virtualization/clusters/3/", + "name": "cluster-abc" + }, + "role": { + "id": 4, + "url": "http://localhost:8000/api/dcim/device-roles/4/", + "name": "server", + "slug": "server" + }, + "tenant": null, + "platform": { + "id": 4, + "url": "http://localhost:8000/api/dcim/platforms/4/", + "name": "PanOS", + "slug": "panos" + }, + "primary_ip": { + "id": 18, + "url": "http://localhost:8000/api/ipam/ip-addresses/18/", + "family": 4, + "address": "55.1.0.3/27" + }, + "primary_ip4": { + "id": 18, + "url": "http://localhost:8000/api/ipam/ip-addresses/18/", + "family": 4, + "address": "55.1.0.3/27" + }, + "primary_ip6": null, + "vcpus": 8, + "memory": 8000, + "disk": 500, + "comments": "", + "local_context_data": { + "index": 12, + "testbed": "abcd" + }, + "tags": [ + "pano" + ], + "custom_fields": {}, + "config_context": { + "index": 12, + "testbed": "abcd" + }, + "created": "2020-09-06", + "last_updated": "2020-09-06T05:03:36.371611Z" + }, + "hostname": "55.1.0.3", + "port": null, + "username": null, + "password": null, + "platform": "PanOS" + }, + "tx-pa100-fw1": { + "name": "tx-pa100-fw1", + "connection_options": {}, + "groups": [], + "data": { + "id": 5, + "name": "tx-pa100-fw1", + "status": { + "value": "active", + "label": "Active", + "id": 1 + }, + "site": null, + "cluster": { + "id": 3, + "url": "http://localhost:8000/api/virtualization/clusters/3/", + "name": "cluster-abc" + }, + "role": { + "id": 4, + "url": "http://localhost:8000/api/dcim/device-roles/4/", + "name": "server", + "slug": "server" + }, + "tenant": null, + "platform": { + "id": 4, + "url": "http://localhost:8000/api/dcim/platforms/4/", + "name": "PanOS", + "slug": "panos" + }, + "primary_ip": { + "id": 19, + "url": "http://localhost:8000/api/ipam/ip-addresses/19/", + "family": 4, + "address": "16.52.0.1/17" + }, + "primary_ip4": { + "id": 19, + "url": "http://localhost:8000/api/ipam/ip-addresses/19/", + "family": 4, + "address": "16.52.0.1/17" + }, + "primary_ip6": null, + "vcpus": 4, + "memory": 4000, + "disk": 500, + "comments": "", + "local_context_data": { + "bgp_as": 65101, + "interfaces": [ + "ethernet1", + "ethernet2", + "ethernet3" + ] + }, + "tags": [ + "border", + "firewall" + ], + "custom_fields": {}, + "config_context": { + "bgp_as": 65101, + "interfaces": [ + "ethernet1", + "ethernet2", + "ethernet3" + ] + }, + "created": "2020-09-06", + "last_updated": "2020-09-06T05:03:52.392577Z" + }, + "hostname": "16.52.0.1", + "port": null, + "username": null, + "password": null, + "platform": "PanOS" + }, + "tx-scout-1": { + "name": "tx-scout-1", + "connection_options": {}, + "groups": [], + "data": { + "id": 3, + "name": "tx-scout-1", + "status": { + "value": "active", + "label": "Active", + "id": 1 + }, + "site": null, + "cluster": { + "id": 2, + "url": "http://localhost:8000/api/virtualization/clusters/2/", + "name": "cluster-xyz" + }, + "role": { + "id": 4, + "url": "http://localhost:8000/api/dcim/device-roles/4/", + "name": "server", + "slug": "server" + }, + "tenant": null, + "platform": { + "id": 7, + "url": "http://localhost:8000/api/dcim/platforms/7/", + "name": "Scout", + "slug": "scout" + }, + "primary_ip": { + "id": 20, + "url": "http://localhost:8000/api/ipam/ip-addresses/20/", + "family": 4, + "address": "66.102.99.6/29" + }, + "primary_ip4": { + "id": 20, + "url": "http://localhost:8000/api/ipam/ip-addresses/20/", + "family": 4, + "address": "66.102.99.6/29" + }, + "primary_ip6": null, + "vcpus": 7, + "memory": 8000, + "disk": 500, + "comments": "", + "local_context_data": { + "mode": "monitor", + "role": "prod" + }, + "tags": [ + "tag55" + ], + "custom_fields": {}, + "config_context": { + "mode": "monitor", + "role": "prod" + }, + "created": "2020-09-06", + "last_updated": "2020-09-06T05:03:03.629883Z" + }, + "hostname": "66.102.99.6", + "port": null, + "username": null, + "password": null, + "platform": "Scout" + } + }, + "groups": {}, + "defaults": { + "data": {}, + "connection_options": {}, + "hostname": null, + "port": null, + "username": null, + "password": null, + "platform": null + } + } \ No newline at end of file diff --git a/tests/mocked/2.8.9/virtual-machines-0.json b/tests/mocked/2.8.9/virtual-machines-0.json new file mode 100644 index 0000000..1eb8699 --- /dev/null +++ b/tests/mocked/2.8.9/virtual-machines-0.json @@ -0,0 +1,72 @@ +{ + "count": 1, + "next": "http://localhost:8080/api/virtualization/virtual-machines/?limit=0&offset=1", + "previous": null, + "results": [ + { + "id": 4, + "name": "ca-expedition-10", + "status": { + "value": "active", + "label": "Active", + "id": 1 + }, + "site": null, + "cluster": { + "id": 3, + "url": "http://localhost:8000/api/virtualization/clusters/3/", + "name": "cluster-abc" + }, + "role": { + "id": 4, + "url": "http://localhost:8000/api/dcim/device-roles/4/", + "name": "server", + "slug": "server" + }, + "tenant": null, + "platform": { + "id": 8, + "url": "http://localhost:8000/api/dcim/platforms/8/", + "name": "Expedition", + "slug": "expedition" + }, + "primary_ip": { + "id": 6, + "url": "http://localhost:8000/api/ipam/ip-addresses/6/", + "family": 4, + "address": "2.2.2.2/24" + }, + "primary_ip4": { + "id": 6, + "url": "http://localhost:8000/api/ipam/ip-addresses/6/", + "family": 4, + "address": "2.2.2.2/24" + }, + "primary_ip6": null, + "vcpus": 12, + "memory": 16000, + "disk": 1000, + "comments": "", + "local_context_data": { + "transition_groups": [ + "ASA-5525", + "ASA-5550", + "ASA-5550-Internet" + ] + }, + "tags": [ + "temporary" + ], + "custom_fields": {}, + "config_context": { + "transition_groups": [ + "ASA-5525", + "ASA-5550", + "ASA-5550-Internet" + ] + }, + "created": "2020-09-06", + "last_updated": "2020-09-06T05:03:17.932973Z" + } + ] +} \ No newline at end of file diff --git a/tests/mocked/2.8.9/virtual-machines-1.json b/tests/mocked/2.8.9/virtual-machines-1.json new file mode 100644 index 0000000..ddfdde7 --- /dev/null +++ b/tests/mocked/2.8.9/virtual-machines-1.json @@ -0,0 +1,66 @@ +{ + "count": 1, + "next": "http://localhost:8080/api/virtualization/virtual-machines/?limit=0&offset=2", + "previous": "http://localhost:8080/api/virtualization/virtual-machines/?limit=0&offset=0", + "results": [ + { + "id": 2, + "name": "ca-panorama-1", + "status": { + "value": "active", + "label": "Active", + "id": 1 + }, + "site": null, + "cluster": { + "id": 3, + "url": "http://localhost:8000/api/virtualization/clusters/3/", + "name": "cluster-abc" + }, + "role": { + "id": 4, + "url": "http://localhost:8000/api/dcim/device-roles/4/", + "name": "server", + "slug": "server" + }, + "tenant": null, + "platform": { + "id": 4, + "url": "http://localhost:8000/api/dcim/platforms/4/", + "name": "PanOS", + "slug": "panos" + }, + "primary_ip": { + "id": 18, + "url": "http://localhost:8000/api/ipam/ip-addresses/18/", + "family": 4, + "address": "55.1.0.3/27" + }, + "primary_ip4": { + "id": 18, + "url": "http://localhost:8000/api/ipam/ip-addresses/18/", + "family": 4, + "address": "55.1.0.3/27" + }, + "primary_ip6": null, + "vcpus": 8, + "memory": 8000, + "disk": 500, + "comments": "", + "local_context_data": { + "index": 12, + "testbed": "abcd" + }, + "tags": [ + "pano" + ], + "custom_fields": {}, + "config_context": { + "index": 12, + "testbed": "abcd" + }, + "created": "2020-09-06", + "last_updated": "2020-09-06T05:03:36.371611Z" + } + ] +} \ No newline at end of file diff --git a/tests/mocked/2.8.9/virtual-machines-2.json b/tests/mocked/2.8.9/virtual-machines-2.json new file mode 100644 index 0000000..1334777 --- /dev/null +++ b/tests/mocked/2.8.9/virtual-machines-2.json @@ -0,0 +1,134 @@ +{ + "count": 2, + "next": null, + "previous": "http://localhost:8080/api/virtualization/virtual-machines/?limit=0&offset=1", + "results": [ + { + "id": 5, + "name": "tx-pa100-fw1", + "status": { + "value": "active", + "label": "Active", + "id": 1 + }, + "site": null, + "cluster": { + "id": 3, + "url": "http://localhost:8000/api/virtualization/clusters/3/", + "name": "cluster-abc" + }, + "role": { + "id": 4, + "url": "http://localhost:8000/api/dcim/device-roles/4/", + "name": "server", + "slug": "server" + }, + "tenant": null, + "platform": { + "id": 4, + "url": "http://localhost:8000/api/dcim/platforms/4/", + "name": "PanOS", + "slug": "panos" + }, + "primary_ip": { + "id": 19, + "url": "http://localhost:8000/api/ipam/ip-addresses/19/", + "family": 4, + "address": "16.52.0.1/17" + }, + "primary_ip4": { + "id": 19, + "url": "http://localhost:8000/api/ipam/ip-addresses/19/", + "family": 4, + "address": "16.52.0.1/17" + }, + "primary_ip6": null, + "vcpus": 4, + "memory": 4000, + "disk": 500, + "comments": "", + "local_context_data": { + "bgp_as": 65101, + "interfaces": [ + "ethernet1", + "ethernet2", + "ethernet3" + ] + }, + "tags": [ + "border", + "firewall" + ], + "custom_fields": {}, + "config_context": { + "bgp_as": 65101, + "interfaces": [ + "ethernet1", + "ethernet2", + "ethernet3" + ] + }, + "created": "2020-09-06", + "last_updated": "2020-09-06T05:03:52.392577Z" + }, + { + "id": 3, + "name": "tx-scout-1", + "status": { + "value": "active", + "label": "Active", + "id": 1 + }, + "site": null, + "cluster": { + "id": 2, + "url": "http://localhost:8000/api/virtualization/clusters/2/", + "name": "cluster-xyz" + }, + "role": { + "id": 4, + "url": "http://localhost:8000/api/dcim/device-roles/4/", + "name": "server", + "slug": "server" + }, + "tenant": null, + "platform": { + "id": 7, + "url": "http://localhost:8000/api/dcim/platforms/7/", + "name": "Scout", + "slug": "scout" + }, + "primary_ip": { + "id": 20, + "url": "http://localhost:8000/api/ipam/ip-addresses/20/", + "family": 4, + "address": "66.102.99.6/29" + }, + "primary_ip4": { + "id": 20, + "url": "http://localhost:8000/api/ipam/ip-addresses/20/", + "family": 4, + "address": "66.102.99.6/29" + }, + "primary_ip6": null, + "vcpus": 7, + "memory": 8000, + "disk": 500, + "comments": "", + "local_context_data": { + "mode": "monitor", + "role": "prod" + }, + "tags": [ + "tag55" + ], + "custom_fields": {}, + "config_context": { + "mode": "monitor", + "role": "prod" + }, + "created": "2020-09-06", + "last_updated": "2020-09-06T05:03:03.629883Z" + } + ] +} \ No newline at end of file diff --git a/tests/mocked/2.8.9/virtual-machines.json b/tests/mocked/2.8.9/virtual-machines.json new file mode 100644 index 0000000..7e1cc97 --- /dev/null +++ b/tests/mocked/2.8.9/virtual-machines.json @@ -0,0 +1,258 @@ +{ + "count": 4, + "next": null, + "previous": null, + "results": [ + { + "id": 4, + "name": "ca-expedition-10", + "status": { + "value": "active", + "label": "Active", + "id": 1 + }, + "site": null, + "cluster": { + "id": 3, + "url": "http://localhost:8000/api/virtualization/clusters/3/", + "name": "cluster-abc" + }, + "role": { + "id": 4, + "url": "http://localhost:8000/api/dcim/device-roles/4/", + "name": "server", + "slug": "server" + }, + "tenant": null, + "platform": { + "id": 8, + "url": "http://localhost:8000/api/dcim/platforms/8/", + "name": "Expedition", + "slug": "expedition" + }, + "primary_ip": { + "id": 6, + "url": "http://localhost:8000/api/ipam/ip-addresses/6/", + "family": 4, + "address": "2.2.2.2/24" + }, + "primary_ip4": { + "id": 6, + "url": "http://localhost:8000/api/ipam/ip-addresses/6/", + "family": 4, + "address": "2.2.2.2/24" + }, + "primary_ip6": null, + "vcpus": 12, + "memory": 16000, + "disk": 1000, + "comments": "", + "local_context_data": { + "transition_groups": [ + "ASA-5525", + "ASA-5550", + "ASA-5550-Internet" + ] + }, + "tags": [ + "temporary" + ], + "custom_fields": {}, + "config_context": { + "transition_groups": [ + "ASA-5525", + "ASA-5550", + "ASA-5550-Internet" + ] + }, + "created": "2020-09-06", + "last_updated": "2020-09-06T05:03:17.932973Z" + }, + { + "id": 2, + "name": "ca-panorama-1", + "status": { + "value": "active", + "label": "Active", + "id": 1 + }, + "site": null, + "cluster": { + "id": 3, + "url": "http://localhost:8000/api/virtualization/clusters/3/", + "name": "cluster-abc" + }, + "role": { + "id": 4, + "url": "http://localhost:8000/api/dcim/device-roles/4/", + "name": "server", + "slug": "server" + }, + "tenant": null, + "platform": { + "id": 4, + "url": "http://localhost:8000/api/dcim/platforms/4/", + "name": "PanOS", + "slug": "panos" + }, + "primary_ip": { + "id": 18, + "url": "http://localhost:8000/api/ipam/ip-addresses/18/", + "family": 4, + "address": "55.1.0.3/27" + }, + "primary_ip4": { + "id": 18, + "url": "http://localhost:8000/api/ipam/ip-addresses/18/", + "family": 4, + "address": "55.1.0.3/27" + }, + "primary_ip6": null, + "vcpus": 8, + "memory": 8000, + "disk": 500, + "comments": "", + "local_context_data": { + "index": 12, + "testbed": "abcd" + }, + "tags": [ + "pano" + ], + "custom_fields": {}, + "config_context": { + "index": 12, + "testbed": "abcd" + }, + "created": "2020-09-06", + "last_updated": "2020-09-06T05:03:36.371611Z" + }, + { + "id": 5, + "name": "tx-pa100-fw1", + "status": { + "value": "active", + "label": "Active", + "id": 1 + }, + "site": null, + "cluster": { + "id": 3, + "url": "http://localhost:8000/api/virtualization/clusters/3/", + "name": "cluster-abc" + }, + "role": { + "id": 4, + "url": "http://localhost:8000/api/dcim/device-roles/4/", + "name": "server", + "slug": "server" + }, + "tenant": null, + "platform": { + "id": 4, + "url": "http://localhost:8000/api/dcim/platforms/4/", + "name": "PanOS", + "slug": "panos" + }, + "primary_ip": { + "id": 19, + "url": "http://localhost:8000/api/ipam/ip-addresses/19/", + "family": 4, + "address": "16.52.0.1/17" + }, + "primary_ip4": { + "id": 19, + "url": "http://localhost:8000/api/ipam/ip-addresses/19/", + "family": 4, + "address": "16.52.0.1/17" + }, + "primary_ip6": null, + "vcpus": 4, + "memory": 4000, + "disk": 500, + "comments": "", + "local_context_data": { + "bgp_as": 65101, + "interfaces": [ + "ethernet1", + "ethernet2", + "ethernet3" + ] + }, + "tags": [ + "border", + "firewall" + ], + "custom_fields": {}, + "config_context": { + "bgp_as": 65101, + "interfaces": [ + "ethernet1", + "ethernet2", + "ethernet3" + ] + }, + "created": "2020-09-06", + "last_updated": "2020-09-06T05:03:52.392577Z" + }, + { + "id": 3, + "name": "tx-scout-1", + "status": { + "value": "active", + "label": "Active", + "id": 1 + }, + "site": null, + "cluster": { + "id": 2, + "url": "http://localhost:8000/api/virtualization/clusters/2/", + "name": "cluster-xyz" + }, + "role": { + "id": 4, + "url": "http://localhost:8000/api/dcim/device-roles/4/", + "name": "server", + "slug": "server" + }, + "tenant": null, + "platform": { + "id": 7, + "url": "http://localhost:8000/api/dcim/platforms/7/", + "name": "Scout", + "slug": "scout" + }, + "primary_ip": { + "id": 20, + "url": "http://localhost:8000/api/ipam/ip-addresses/20/", + "family": 4, + "address": "66.102.99.6/29" + }, + "primary_ip4": { + "id": 20, + "url": "http://localhost:8000/api/ipam/ip-addresses/20/", + "family": 4, + "address": "66.102.99.6/29" + }, + "primary_ip6": null, + "vcpus": 7, + "memory": 8000, + "disk": 500, + "comments": "", + "local_context_data": { + "mode": "monitor", + "role": "prod" + }, + "tags": [ + "tag55" + ], + "custom_fields": {}, + "config_context": { + "mode": "monitor", + "role": "prod" + }, + "created": "2020-09-06", + "last_updated": "2020-09-06T05:03:03.629883Z" + } + ] +} \ No newline at end of file diff --git a/tests/test_netbox.py b/tests/test_netbox.py index 20babab..8e208e3 100644 --- a/tests/test_netbox.py +++ b/tests/test_netbox.py @@ -12,24 +12,37 @@ VERSIONS = ["2.3.5", "2.8.9"] -def get_inv(requests_mock, plugin, pagination, version, **kwargs): +def _create_mock(requests_mock, pagination, version, application, resource, **kwargs): + """initialises mock objects for testcase""" if not pagination: - with open(f"{BASE_PATH}/mocked/{version}/devices.json", "r") as f: + with open(f"{BASE_PATH}/mocked/{version}/{resource}.json", "r") as f: requests_mock.get( - "http://localhost:8080/api/dcim/devices/?limit=0", + f"http://localhost:8080/api/{application}/{resource}/?limit=0", json=json.load(f), headers={"Content-type": "application/json"}, ) else: for offset in range(3): - with open(f"{BASE_PATH}/mocked/{version}/devices-{offset}.json", "r") as f: - url = "http://localhost:8080/api/dcim/devices/?limit=0" + with open( + f"{BASE_PATH}/mocked/{version}/{resource}-{offset}.json", "r" + ) as f: + url = f"http://localhost:8080/api/{application}/{resource}/?limit=0" requests_mock.get( f"{url}&offset={offset}" if offset else url, json=json.load(f), headers={"Content-type": "application/json"}, ) - return plugin().load() + + +def get_inv(requests_mock, plugin, pagination, version, **kwargs): + + _create_mock(requests_mock, pagination, version, "dcim", "devices") + if kwargs.get("include_vms", None): + _create_mock( + requests_mock, pagination, version, "virtualization", "virtual-machines" + ) + + return plugin(**kwargs).load() class TestNBInventory(object): @@ -46,7 +59,7 @@ def test_inventory(self, requests_mock, version): @pytest.mark.parametrize("version", VERSIONS) def test_inventory_pagination(self, requests_mock, version): - inv = get_inv(requests_mock, self.plugin, False, version) + inv = get_inv(requests_mock, self.plugin, True, version) with open( f"{BASE_PATH}/{self.plugin.__name__}/{version}/expected.json", "r" ) as f: @@ -56,3 +69,22 @@ def test_inventory_pagination(self, requests_mock, version): class TestNetBoxInventory2(TestNBInventory): plugin = NetBoxInventory2 + + # only on NetBoxInventory2 and NetBox 2.8.9 + @pytest.mark.parametrize("version", ["2.8.9"]) + def test_inventory_include_vms(self, requests_mock, version): + inv = get_inv(requests_mock, self.plugin, False, version, include_vms=True) + with open( + f"{BASE_PATH}/{self.plugin.__name__}/{version}/vms-expected.json", "r" + ) as f: + expected = json.load(f) + assert expected == inv.dict() + + @pytest.mark.parametrize("version", ["2.8.9"]) + def test_inventory_include_vms_pagination(self, requests_mock, version): + inv = get_inv(requests_mock, self.plugin, True, version, include_vms=True) + with open( + f"{BASE_PATH}/{self.plugin.__name__}/{version}/vms-expected.json", "r" + ) as f: + expected = json.load(f) + assert expected == inv.dict() From 0ca1367a7b9527a478016b8eb9aa7d7884d7f0b9 Mon Sep 17 00:00:00 2001 From: Wim Van Deun <7521270+enzzzy@users.noreply.github.com> Date: Mon, 23 Nov 2020 00:25:45 +0100 Subject: [PATCH 2/5] fix github workflow deprecating set-env, add-path commands https://github.blog/changelog/2020-10-01-github-actions-deprecating-set-env-and-add-path-commands/ --- .github/workflows/main.yaml | 42 ++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 0bb03cf..6d999a0 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -12,23 +12,22 @@ jobs: uses: actions/setup-python@v1 - name: Install Poetry - uses: dschep/install-poetry-action@v1.2 + uses: snok/install-poetry@v1.1.1 + with: + version: 1.1.4 + virtualenvs-create: true + virtualenvs-in-project: true - name: Cache Poetry virtualenv - uses: actions/cache@v1 - id: cache + uses: actions/cache@v2 + id: cached-poetry-dependencies with: - path: ~/.virtualenvs - key: poetry-linters-${{ hashFiles('**/poetry.lock') }} - restore-keys: poetry-linters-${{ hashFiles('**/poetry.lock') }} - - - name: Set Poetry config - run: | - poetry config virtualenvs.in-project false - poetry config virtualenvs.path ~/.virtualenvs + path: .venv + key: venv-${{ runner.os }}-${{ hashFiles('**/poetry.lock') }} - name: Install Dependencies run: poetry install + if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' - name: Run pylama run: make pylama @@ -52,23 +51,22 @@ jobs: architecture: x64 - name: Install Poetry - uses: dschep/install-poetry-action@v1.2 + uses: snok/install-poetry@v1.1.1 + with: + version: 1.1.4 + virtualenvs-create: true + virtualenvs-in-project: true - name: Cache Poetry virtualenv - uses: actions/cache@v1 - id: cache + uses: actions/cache@v2 + id: cached-poetry-dependencies with: - path: ~/.virtualenvs - key: poetry-${{ matrix.python-version }}-${{ hashFiles('**/poetry.lock') }} - restore-keys: poetry-${{ matrix.python-version }}-${{ hashFiles('**/poetry.lock') }} - - - name: Set Poetry config - run: | - poetry config virtualenvs.in-project false - poetry config virtualenvs.path ~/.virtualenvs + path: .venv + key: venv-${{ runner.os }}-${{ hashFiles('**/poetry.lock') }} - name: Install Dependencies run: poetry install + if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' - name: Run pytest run: make pytest From 75d8ef579e612988aec7bc7d35f1a97df054fba7 Mon Sep 17 00:00:00 2001 From: Wim Van Deun <7521270+enzzzy@users.noreply.github.com> Date: Sat, 7 Nov 2020 00:44:43 +0100 Subject: [PATCH 3/5] implement use_platform_slug for NetBoxInventory2 --- nornir_netbox/plugins/inventory/netbox.py | 17 +- tests/NetBoxInventory2/2.8.9/expected.json | 12 +- .../2.8.9/expected_use_platform_slug.json | 319 +++++++++ .../NetBoxInventory2/2.8.9/vms-expected.json | 14 +- .../2.8.9/vms-expected_use_platform_slug.json | 610 ++++++++++++++++++ tests/mocked/2.8.9/devices-0.json | 2 +- tests/mocked/2.8.9/devices-2.json | 4 +- tests/mocked/2.8.9/devices.json | 6 +- tests/test_netbox.py | 29 + 9 files changed, 988 insertions(+), 25 deletions(-) create mode 100644 tests/NetBoxInventory2/2.8.9/expected_use_platform_slug.json create mode 100644 tests/NetBoxInventory2/2.8.9/vms-expected_use_platform_slug.json diff --git a/nornir_netbox/plugins/inventory/netbox.py b/nornir_netbox/plugins/inventory/netbox.py index 941b074..fdd0c25 100644 --- a/nornir_netbox/plugins/inventory/netbox.py +++ b/nornir_netbox/plugins/inventory/netbox.py @@ -185,7 +185,8 @@ class NetBoxInventory2: filter_parameters: Key-value pairs that allow you to filter the NetBox inventory. include_vms: Get virtual machines from NetBox as well as devices. (defaults to False) - + use_platform_slug: Use the NetBox platform slug for the platform attribute of a Host + (defaults to False) """ def __init__( @@ -196,6 +197,7 @@ def __init__( flatten_custom_fields: bool = False, filter_parameters: Optional[Dict[str, Any]] = None, include_vms: bool = False, + use_platform_slug: bool = False, **kwargs: Any, ) -> None: filter_parameters = filter_parameters or {} @@ -208,6 +210,7 @@ def __init__( self.flatten_custom_fields = flatten_custom_fields self.filter_parameters = filter_parameters self.include_vms = include_vms + self.use_platform_slug = use_platform_slug self.session = requests.Session() self.session.headers.update({"Authorization": f"Token {nb_token}"}) @@ -251,11 +254,13 @@ def load(self) -> Inventory: hostname = device["name"] serialized_device["hostname"] = hostname - platform = ( - device["platform"]["name"] - if isinstance(device["platform"], dict) - else device["platform"] - ) + if isinstance(device["platform"], dict) and self.use_platform_slug: + platform = device["platform"].get("slug") + elif isinstance(device["platform"], dict): + platform = device["platform"].get("name") + else: + platform = device["platform"] + serialized_device["platform"] = platform name = serialized_device["data"].get("name") or str( diff --git a/tests/NetBoxInventory2/2.8.9/expected.json b/tests/NetBoxInventory2/2.8.9/expected.json index cff9be3..8921073 100644 --- a/tests/NetBoxInventory2/2.8.9/expected.json +++ b/tests/NetBoxInventory2/2.8.9/expected.json @@ -27,7 +27,7 @@ "platform": { "id": 1, "url": "http://localhost:8080/api/dcim/platforms/1/", - "name": "junos", + "name": "juniper_junos", "slug": "junos" }, "serial": "", @@ -70,7 +70,7 @@ }, "name":"1-Core", "hostname": "10.0.1.1", - "platform": "junos", + "platform": "juniper_junos", "port": null, "username": null, "password": null, @@ -176,7 +176,7 @@ "platform": { "id": 2, "url": "http://localhost:8080/api/dcim/platforms/2/", - "name": "ios", + "name": "cisco_ios", "slug": "ios" }, "serial": "", @@ -221,7 +221,7 @@ }, "name":"3-Access", "hostname": "192.168.3.1", - "platform": "ios", + "platform": "cisco_ios", "port": null, "username": null, "password": null, @@ -255,7 +255,7 @@ "platform": { "id": 1, "url": "http://localhost:8080/api/dcim/platforms/1/", - "name": "junos", + "name": "juniper_junos", "slug": "junos" }, "serial": "", @@ -298,7 +298,7 @@ }, "name":"4", "hostname": "10.0.1.4", - "platform": "junos", + "platform": "juniper_junos", "port": null, "username": null, "password": null, diff --git a/tests/NetBoxInventory2/2.8.9/expected_use_platform_slug.json b/tests/NetBoxInventory2/2.8.9/expected_use_platform_slug.json new file mode 100644 index 0000000..95fc8fe --- /dev/null +++ b/tests/NetBoxInventory2/2.8.9/expected_use_platform_slug.json @@ -0,0 +1,319 @@ +{ + "hosts": { + "1-Core": { + "data": { + "id": 1, + "name": "1-Core", + "display_name": "1-Core", + "device_type": { + "id": 11, + "url": "http://localhost:8080/api/dcim/device-types/11/", + "manufacturer": { + "id": 3, + "url": "http://localhost:8080/api/dcim/manufacturers/3/", + "name": "Juniper", + "slug": "juniper" + }, + "model": "MX480", + "slug": "mx480" + }, + "device_role": { + "id": 1, + "url": "http://localhost:8080/api/dcim/device-roles/1/", + "name": "Router", + "slug": "rt" + }, + "tenant": null, + "platform": { + "id": 1, + "url": "http://localhost:8080/api/dcim/platforms/1/", + "name": "juniper_junos", + "slug": "junos" + }, + "serial": "", + "asset_tag": null, + "site": { + "id": 3, + "url": "http://localhost:8080/api/dcim/sites/3/", + "name": "Sunnyvale, CA", + "slug": "sunnyvale-ca" + }, + "rack": null, + "position": null, + "face": null, + "parent_device": null, + "status": { + "value": 1, + "label": "Active" + }, + "primary_ip": { + "id": 1, + "url": "http://localhost:8080/api/ipam/ip-addresses/1/", + "family": 4, + "address": "10.0.1.1/32" + }, + "primary_ip4": { + "id": 1, + "url": "http://localhost:8080/api/ipam/ip-addresses/1/", + "family": 4, + "address": "10.0.1.1/32" + }, + "primary_ip6": null, + "cluster": null, + "virtual_chassis": null, + "vc_position": null, + "vc_priority": null, + "comments": "", + "custom_fields": {}, + "created": "2018-07-12", + "last_updated": "2018-07-12T11:53:54.742412Z" + }, + "name":"1-Core", + "hostname": "10.0.1.1", + "platform": "junos", + "port": null, + "username": null, + "password": null, + "connection_options": {}, + "groups": [] + }, + "2-Distribution": { + "data": { + "id": 2, + "name": "2-Distribution", + "display_name": "2-Distribution", + "device_type": { + "id": 9, + "url": "http://localhost:8080/api/dcim/device-types/9/", + "manufacturer": { + "id": 3, + "url": "http://localhost:8080/api/dcim/manufacturers/3/", + "name": "Juniper", + "slug": "juniper" + }, + "model": "EX4550-32F", + "slug": "ex4550-32f" + }, + "device_role": { + "id": 1, + "url": "http://localhost:8080/api/dcim/device-roles/1/", + "name": "Router", + "slug": "rt" + }, + "tenant": null, + "platform": null, + "serial": "", + "asset_tag": null, + "site": { + "id": 3, + "url": "http://localhost:8080/api/dcim/sites/3/", + "name": "Sunnyvale, CA", + "slug": "sunnyvale-ca" + }, + "rack": null, + "position": null, + "face": null, + "parent_device": null, + "status": { + "value": 1, + "label": "Active" + }, + "primary_ip": { + "id": 2, + "url": "http://localhost:8080/api/ipam/ip-addresses/2/", + "family": 4, + "address": "172.16.2.1/32" + }, + "primary_ip4": { + "id": 2, + "url": "http://localhost:8080/api/ipam/ip-addresses/2/", + "family": 4, + "address": "172.16.2.1/32" + }, + "primary_ip6": null, + "cluster": null, + "virtual_chassis": null, + "vc_position": null, + "vc_priority": null, + "comments": "", + "custom_fields": {}, + "created": "2018-07-12", + "last_updated": "2018-07-12T11:53:54.802934Z" + }, + "name":"2-Distribution", + "hostname": "172.16.2.1", + "platform": null, + "port": null, + "username": null, + "password": null, + "connection_options": {}, + "groups": [] + }, + "3-Access": { + "data": { + "id": 3, + "name": "3-Access", + "display_name": "3-Access", + "device_type": { + "id": 2, + "url": "http://localhost:8080/api/dcim/device-types/2/", + "manufacturer": { + "id": 2, + "url": "http://localhost:8080/api/dcim/manufacturers/2/", + "name": "Cisco", + "slug": "cisco" + }, + "model": "3650-48TQ-L", + "slug": "3650-48tq-l" + }, + "device_role": { + "id": 2, + "url": "http://localhost:8080/api/dcim/device-roles/2/", + "name": "Switch", + "slug": "sw" + }, + "tenant": null, + "platform": { + "id": 2, + "url": "http://localhost:8080/api/dcim/platforms/2/", + "name": "cisco_ios", + "slug": "ios" + }, + "serial": "", + "asset_tag": null, + "site": { + "id": 2, + "url": "http://localhost:8080/api/dcim/sites/2/", + "name": "San Jose, CA", + "slug": "san-jose-ca" + }, + "rack": null, + "position": null, + "face": null, + "parent_device": null, + "status": { + "value": 1, + "label": "Active" + }, + "primary_ip": { + "id": 3, + "url": "http://localhost:8080/api/ipam/ip-addresses/3/", + "family": 4, + "address": "192.168.3.1/32" + }, + "primary_ip4": { + "id": 3, + "url": "http://localhost:8080/api/ipam/ip-addresses/3/", + "family": 4, + "address": "192.168.3.1/32" + }, + "primary_ip6": null, + "cluster": null, + "virtual_chassis": null, + "vc_position": null, + "vc_priority": null, + "comments": "", + "custom_fields": { + "user_defined": 1 + }, + "created": "2018-07-12", + "last_updated": "2018-07-12T11:53:54.866133Z" + }, + "name":"3-Access", + "hostname": "192.168.3.1", + "platform": "ios", + "port": null, + "username": null, + "password": null, + "connection_options": {}, + "groups": [] + }, + "4": { + "data": { + "id": 4, + "name": null, + "display_name": "MX480", + "device_type": { + "id": 11, + "url": "http://localhost:8080/api/dcim/device-types/11/", + "manufacturer": { + "id": 3, + "url": "http://localhost:8080/api/dcim/manufacturers/3/", + "name": "Juniper", + "slug": "juniper" + }, + "model": "MX480", + "slug": "mx480" + }, + "device_role": { + "id": 1, + "url": "http://localhost:8080/api/dcim/device-roles/1/", + "name": "Router", + "slug": "rt" + }, + "tenant": null, + "platform": { + "id": 1, + "url": "http://localhost:8080/api/dcim/platforms/1/", + "name": "juniper_junos", + "slug": "junos" + }, + "serial": "", + "asset_tag": null, + "site": { + "id": 3, + "url": "http://localhost:8080/api/dcim/sites/3/", + "name": "Sunnyvale, CA", + "slug": "sunnyvale-ca" + }, + "rack": null, + "position": null, + "face": null, + "parent_device": null, + "status": { + "value": 1, + "label": "Active" + }, + "primary_ip": { + "id": 4, + "url": "http://localhost:8080/api/ipam/ip-addresses/4/", + "family": 4, + "address": "10.0.1.4/32" + }, + "primary_ip4": { + "id": 1, + "url": "http://localhost:8080/api/ipam/ip-addresses/4/", + "family": 4, + "address": "10.0.1.4/32" + }, + "primary_ip6": null, + "cluster": null, + "virtual_chassis": null, + "vc_position": null, + "vc_priority": null, + "comments": "", + "custom_fields": {}, + "created": "2018-07-12", + "last_updated": "2018-07-12T11:53:54.742412Z" + }, + "name":"4", + "hostname": "10.0.1.4", + "platform": "junos", + "port": null, + "username": null, + "password": null, + "connection_options": {}, + "groups": [] + } + }, + "groups": {}, + "defaults": { + "port": null, + "hostname": null, + "username": null, + "password": null, + "platform": null, + "data": {}, + "connection_options": {} + } +} diff --git a/tests/NetBoxInventory2/2.8.9/vms-expected.json b/tests/NetBoxInventory2/2.8.9/vms-expected.json index 6d34d39..bc19733 100644 --- a/tests/NetBoxInventory2/2.8.9/vms-expected.json +++ b/tests/NetBoxInventory2/2.8.9/vms-expected.json @@ -30,7 +30,7 @@ "platform": { "id": 1, "url": "http://localhost:8080/api/dcim/platforms/1/", - "name": "junos", + "name": "juniper_junos", "slug": "junos" }, "serial": "", @@ -75,7 +75,7 @@ "port": null, "username": null, "password": null, - "platform": "junos" + "platform": "juniper_junos" }, "1-Core": { "name": "1-Core", @@ -107,7 +107,7 @@ "platform": { "id": 1, "url": "http://localhost:8080/api/dcim/platforms/1/", - "name": "junos", + "name": "juniper_junos", "slug": "junos" }, "serial": "", @@ -152,7 +152,7 @@ "port": null, "username": null, "password": null, - "platform": "junos" + "platform": "juniper_junos" }, "2-Distribution": { "name": "2-Distribution", @@ -256,7 +256,7 @@ "platform": { "id": 2, "url": "http://localhost:8080/api/dcim/platforms/2/", - "name": "ios", + "name": "cisco_ios", "slug": "ios" }, "serial": "", @@ -303,7 +303,7 @@ "port": null, "username": null, "password": null, - "platform": "ios" + "platform": "cisco_ios" }, "ca-expedition-10": { "name": "ca-expedition-10", @@ -607,4 +607,4 @@ "password": null, "platform": null } - } \ No newline at end of file + } diff --git a/tests/NetBoxInventory2/2.8.9/vms-expected_use_platform_slug.json b/tests/NetBoxInventory2/2.8.9/vms-expected_use_platform_slug.json new file mode 100644 index 0000000..4fde829 --- /dev/null +++ b/tests/NetBoxInventory2/2.8.9/vms-expected_use_platform_slug.json @@ -0,0 +1,610 @@ +{ + "hosts": { + "4": { + "name": "4", + "connection_options": {}, + "groups": [], + "data": { + "id": 4, + "name": null, + "display_name": "MX480", + "device_type": { + "id": 11, + "url": "http://localhost:8080/api/dcim/device-types/11/", + "manufacturer": { + "id": 3, + "url": "http://localhost:8080/api/dcim/manufacturers/3/", + "name": "Juniper", + "slug": "juniper" + }, + "model": "MX480", + "slug": "mx480" + }, + "device_role": { + "id": 1, + "url": "http://localhost:8080/api/dcim/device-roles/1/", + "name": "Router", + "slug": "rt" + }, + "tenant": null, + "platform": { + "id": 1, + "url": "http://localhost:8080/api/dcim/platforms/1/", + "name": "juniper_junos", + "slug": "junos" + }, + "serial": "", + "asset_tag": null, + "site": { + "id": 3, + "url": "http://localhost:8080/api/dcim/sites/3/", + "name": "Sunnyvale, CA", + "slug": "sunnyvale-ca" + }, + "rack": null, + "position": null, + "face": null, + "parent_device": null, + "status": { + "value": 1, + "label": "Active" + }, + "primary_ip": { + "id": 4, + "url": "http://localhost:8080/api/ipam/ip-addresses/4/", + "family": 4, + "address": "10.0.1.4/32" + }, + "primary_ip4": { + "id": 1, + "url": "http://localhost:8080/api/ipam/ip-addresses/4/", + "family": 4, + "address": "10.0.1.4/32" + }, + "primary_ip6": null, + "cluster": null, + "virtual_chassis": null, + "vc_position": null, + "vc_priority": null, + "comments": "", + "custom_fields": {}, + "created": "2018-07-12", + "last_updated": "2018-07-12T11:53:54.742412Z" + }, + "hostname": "10.0.1.4", + "port": null, + "username": null, + "password": null, + "platform": "junos" + }, + "1-Core": { + "name": "1-Core", + "connection_options": {}, + "groups": [], + "data": { + "id": 1, + "name": "1-Core", + "display_name": "1-Core", + "device_type": { + "id": 11, + "url": "http://localhost:8080/api/dcim/device-types/11/", + "manufacturer": { + "id": 3, + "url": "http://localhost:8080/api/dcim/manufacturers/3/", + "name": "Juniper", + "slug": "juniper" + }, + "model": "MX480", + "slug": "mx480" + }, + "device_role": { + "id": 1, + "url": "http://localhost:8080/api/dcim/device-roles/1/", + "name": "Router", + "slug": "rt" + }, + "tenant": null, + "platform": { + "id": 1, + "url": "http://localhost:8080/api/dcim/platforms/1/", + "name": "juniper_junos", + "slug": "junos" + }, + "serial": "", + "asset_tag": null, + "site": { + "id": 3, + "url": "http://localhost:8080/api/dcim/sites/3/", + "name": "Sunnyvale, CA", + "slug": "sunnyvale-ca" + }, + "rack": null, + "position": null, + "face": null, + "parent_device": null, + "status": { + "value": 1, + "label": "Active" + }, + "primary_ip": { + "id": 1, + "url": "http://localhost:8080/api/ipam/ip-addresses/1/", + "family": 4, + "address": "10.0.1.1/32" + }, + "primary_ip4": { + "id": 1, + "url": "http://localhost:8080/api/ipam/ip-addresses/1/", + "family": 4, + "address": "10.0.1.1/32" + }, + "primary_ip6": null, + "cluster": null, + "virtual_chassis": null, + "vc_position": null, + "vc_priority": null, + "comments": "", + "custom_fields": {}, + "created": "2018-07-12", + "last_updated": "2018-07-12T11:53:54.742412Z" + }, + "hostname": "10.0.1.1", + "port": null, + "username": null, + "password": null, + "platform": "junos" + }, + "2-Distribution": { + "name": "2-Distribution", + "connection_options": {}, + "groups": [], + "data": { + "id": 2, + "name": "2-Distribution", + "display_name": "2-Distribution", + "device_type": { + "id": 9, + "url": "http://localhost:8080/api/dcim/device-types/9/", + "manufacturer": { + "id": 3, + "url": "http://localhost:8080/api/dcim/manufacturers/3/", + "name": "Juniper", + "slug": "juniper" + }, + "model": "EX4550-32F", + "slug": "ex4550-32f" + }, + "device_role": { + "id": 1, + "url": "http://localhost:8080/api/dcim/device-roles/1/", + "name": "Router", + "slug": "rt" + }, + "tenant": null, + "platform": null, + "serial": "", + "asset_tag": null, + "site": { + "id": 3, + "url": "http://localhost:8080/api/dcim/sites/3/", + "name": "Sunnyvale, CA", + "slug": "sunnyvale-ca" + }, + "rack": null, + "position": null, + "face": null, + "parent_device": null, + "status": { + "value": 1, + "label": "Active" + }, + "primary_ip": { + "id": 2, + "url": "http://localhost:8080/api/ipam/ip-addresses/2/", + "family": 4, + "address": "172.16.2.1/32" + }, + "primary_ip4": { + "id": 2, + "url": "http://localhost:8080/api/ipam/ip-addresses/2/", + "family": 4, + "address": "172.16.2.1/32" + }, + "primary_ip6": null, + "cluster": null, + "virtual_chassis": null, + "vc_position": null, + "vc_priority": null, + "comments": "", + "custom_fields": {}, + "created": "2018-07-12", + "last_updated": "2018-07-12T11:53:54.802934Z" + }, + "hostname": "172.16.2.1", + "port": null, + "username": null, + "password": null, + "platform": null + }, + "3-Access": { + "name": "3-Access", + "connection_options": {}, + "groups": [], + "data": { + "id": 3, + "name": "3-Access", + "display_name": "3-Access", + "device_type": { + "id": 2, + "url": "http://localhost:8080/api/dcim/device-types/2/", + "manufacturer": { + "id": 2, + "url": "http://localhost:8080/api/dcim/manufacturers/2/", + "name": "Cisco", + "slug": "cisco" + }, + "model": "3650-48TQ-L", + "slug": "3650-48tq-l" + }, + "device_role": { + "id": 2, + "url": "http://localhost:8080/api/dcim/device-roles/2/", + "name": "Switch", + "slug": "sw" + }, + "tenant": null, + "platform": { + "id": 2, + "url": "http://localhost:8080/api/dcim/platforms/2/", + "name": "cisco_ios", + "slug": "ios" + }, + "serial": "", + "asset_tag": null, + "site": { + "id": 2, + "url": "http://localhost:8080/api/dcim/sites/2/", + "name": "San Jose, CA", + "slug": "san-jose-ca" + }, + "rack": null, + "position": null, + "face": null, + "parent_device": null, + "status": { + "value": 1, + "label": "Active" + }, + "primary_ip": { + "id": 3, + "url": "http://localhost:8080/api/ipam/ip-addresses/3/", + "family": 4, + "address": "192.168.3.1/32" + }, + "primary_ip4": { + "id": 3, + "url": "http://localhost:8080/api/ipam/ip-addresses/3/", + "family": 4, + "address": "192.168.3.1/32" + }, + "primary_ip6": null, + "cluster": null, + "virtual_chassis": null, + "vc_position": null, + "vc_priority": null, + "comments": "", + "custom_fields": { + "user_defined": 1 + }, + "created": "2018-07-12", + "last_updated": "2018-07-12T11:53:54.866133Z" + }, + "hostname": "192.168.3.1", + "port": null, + "username": null, + "password": null, + "platform": "ios" + }, + "ca-expedition-10": { + "name": "ca-expedition-10", + "connection_options": {}, + "groups": [], + "data": { + "id": 4, + "name": "ca-expedition-10", + "status": { + "value": "active", + "label": "Active", + "id": 1 + }, + "site": null, + "cluster": { + "id": 3, + "url": "http://localhost:8000/api/virtualization/clusters/3/", + "name": "cluster-abc" + }, + "role": { + "id": 4, + "url": "http://localhost:8000/api/dcim/device-roles/4/", + "name": "server", + "slug": "server" + }, + "tenant": null, + "platform": { + "id": 8, + "url": "http://localhost:8000/api/dcim/platforms/8/", + "name": "Expedition", + "slug": "expedition" + }, + "primary_ip": { + "id": 6, + "url": "http://localhost:8000/api/ipam/ip-addresses/6/", + "family": 4, + "address": "2.2.2.2/24" + }, + "primary_ip4": { + "id": 6, + "url": "http://localhost:8000/api/ipam/ip-addresses/6/", + "family": 4, + "address": "2.2.2.2/24" + }, + "primary_ip6": null, + "vcpus": 12, + "memory": 16000, + "disk": 1000, + "comments": "", + "local_context_data": { + "transition_groups": [ + "ASA-5525", + "ASA-5550", + "ASA-5550-Internet" + ] + }, + "tags": [ + "temporary" + ], + "custom_fields": {}, + "config_context": { + "transition_groups": [ + "ASA-5525", + "ASA-5550", + "ASA-5550-Internet" + ] + }, + "created": "2020-09-06", + "last_updated": "2020-09-06T05:03:17.932973Z" + }, + "hostname": "2.2.2.2", + "port": null, + "username": null, + "password": null, + "platform": "expedition" + }, + "ca-panorama-1": { + "name": "ca-panorama-1", + "connection_options": {}, + "groups": [], + "data": { + "id": 2, + "name": "ca-panorama-1", + "status": { + "value": "active", + "label": "Active", + "id": 1 + }, + "site": null, + "cluster": { + "id": 3, + "url": "http://localhost:8000/api/virtualization/clusters/3/", + "name": "cluster-abc" + }, + "role": { + "id": 4, + "url": "http://localhost:8000/api/dcim/device-roles/4/", + "name": "server", + "slug": "server" + }, + "tenant": null, + "platform": { + "id": 4, + "url": "http://localhost:8000/api/dcim/platforms/4/", + "name": "PanOS", + "slug": "panos" + }, + "primary_ip": { + "id": 18, + "url": "http://localhost:8000/api/ipam/ip-addresses/18/", + "family": 4, + "address": "55.1.0.3/27" + }, + "primary_ip4": { + "id": 18, + "url": "http://localhost:8000/api/ipam/ip-addresses/18/", + "family": 4, + "address": "55.1.0.3/27" + }, + "primary_ip6": null, + "vcpus": 8, + "memory": 8000, + "disk": 500, + "comments": "", + "local_context_data": { + "index": 12, + "testbed": "abcd" + }, + "tags": [ + "pano" + ], + "custom_fields": {}, + "config_context": { + "index": 12, + "testbed": "abcd" + }, + "created": "2020-09-06", + "last_updated": "2020-09-06T05:03:36.371611Z" + }, + "hostname": "55.1.0.3", + "port": null, + "username": null, + "password": null, + "platform": "panos" + }, + "tx-pa100-fw1": { + "name": "tx-pa100-fw1", + "connection_options": {}, + "groups": [], + "data": { + "id": 5, + "name": "tx-pa100-fw1", + "status": { + "value": "active", + "label": "Active", + "id": 1 + }, + "site": null, + "cluster": { + "id": 3, + "url": "http://localhost:8000/api/virtualization/clusters/3/", + "name": "cluster-abc" + }, + "role": { + "id": 4, + "url": "http://localhost:8000/api/dcim/device-roles/4/", + "name": "server", + "slug": "server" + }, + "tenant": null, + "platform": { + "id": 4, + "url": "http://localhost:8000/api/dcim/platforms/4/", + "name": "PanOS", + "slug": "panos" + }, + "primary_ip": { + "id": 19, + "url": "http://localhost:8000/api/ipam/ip-addresses/19/", + "family": 4, + "address": "16.52.0.1/17" + }, + "primary_ip4": { + "id": 19, + "url": "http://localhost:8000/api/ipam/ip-addresses/19/", + "family": 4, + "address": "16.52.0.1/17" + }, + "primary_ip6": null, + "vcpus": 4, + "memory": 4000, + "disk": 500, + "comments": "", + "local_context_data": { + "bgp_as": 65101, + "interfaces": [ + "ethernet1", + "ethernet2", + "ethernet3" + ] + }, + "tags": [ + "border", + "firewall" + ], + "custom_fields": {}, + "config_context": { + "bgp_as": 65101, + "interfaces": [ + "ethernet1", + "ethernet2", + "ethernet3" + ] + }, + "created": "2020-09-06", + "last_updated": "2020-09-06T05:03:52.392577Z" + }, + "hostname": "16.52.0.1", + "port": null, + "username": null, + "password": null, + "platform": "panos" + }, + "tx-scout-1": { + "name": "tx-scout-1", + "connection_options": {}, + "groups": [], + "data": { + "id": 3, + "name": "tx-scout-1", + "status": { + "value": "active", + "label": "Active", + "id": 1 + }, + "site": null, + "cluster": { + "id": 2, + "url": "http://localhost:8000/api/virtualization/clusters/2/", + "name": "cluster-xyz" + }, + "role": { + "id": 4, + "url": "http://localhost:8000/api/dcim/device-roles/4/", + "name": "server", + "slug": "server" + }, + "tenant": null, + "platform": { + "id": 7, + "url": "http://localhost:8000/api/dcim/platforms/7/", + "name": "Scout", + "slug": "scout" + }, + "primary_ip": { + "id": 20, + "url": "http://localhost:8000/api/ipam/ip-addresses/20/", + "family": 4, + "address": "66.102.99.6/29" + }, + "primary_ip4": { + "id": 20, + "url": "http://localhost:8000/api/ipam/ip-addresses/20/", + "family": 4, + "address": "66.102.99.6/29" + }, + "primary_ip6": null, + "vcpus": 7, + "memory": 8000, + "disk": 500, + "comments": "", + "local_context_data": { + "mode": "monitor", + "role": "prod" + }, + "tags": [ + "tag55" + ], + "custom_fields": {}, + "config_context": { + "mode": "monitor", + "role": "prod" + }, + "created": "2020-09-06", + "last_updated": "2020-09-06T05:03:03.629883Z" + }, + "hostname": "66.102.99.6", + "port": null, + "username": null, + "password": null, + "platform": "scout" + } + }, + "groups": {}, + "defaults": { + "data": {}, + "connection_options": {}, + "hostname": null, + "port": null, + "username": null, + "password": null, + "platform": null + } + } diff --git a/tests/mocked/2.8.9/devices-0.json b/tests/mocked/2.8.9/devices-0.json index e804b96..abdce3b 100644 --- a/tests/mocked/2.8.9/devices-0.json +++ b/tests/mocked/2.8.9/devices-0.json @@ -29,7 +29,7 @@ "platform": { "id": 1, "url": "http://localhost:8080/api/dcim/platforms/1/", - "name": "junos", + "name": "juniper_junos", "slug": "junos" }, "serial": "", diff --git a/tests/mocked/2.8.9/devices-2.json b/tests/mocked/2.8.9/devices-2.json index 7331d95..fc6c435 100644 --- a/tests/mocked/2.8.9/devices-2.json +++ b/tests/mocked/2.8.9/devices-2.json @@ -29,7 +29,7 @@ "platform": { "id": 2, "url": "http://localhost:8080/api/dcim/platforms/2/", - "name": "ios", + "name": "cisco_ios", "slug": "ios" }, "serial": "", @@ -96,7 +96,7 @@ "platform": { "id": 1, "url": "http://localhost:8080/api/dcim/platforms/1/", - "name": "junos", + "name": "juniper_junos", "slug": "junos" }, "serial": "", diff --git a/tests/mocked/2.8.9/devices.json b/tests/mocked/2.8.9/devices.json index f083c16..e7eac63 100644 --- a/tests/mocked/2.8.9/devices.json +++ b/tests/mocked/2.8.9/devices.json @@ -29,7 +29,7 @@ "platform": { "id": 1, "url": "http://localhost:8080/api/dcim/platforms/1/", - "name": "junos", + "name": "juniper_junos", "slug": "junos" }, "serial": "", @@ -160,7 +160,7 @@ "platform": { "id": 2, "url": "http://localhost:8080/api/dcim/platforms/2/", - "name": "ios", + "name": "cisco_ios", "slug": "ios" }, "serial": "", @@ -229,7 +229,7 @@ "platform": { "id": 1, "url": "http://localhost:8080/api/dcim/platforms/1/", - "name": "junos", + "name": "juniper_junos", "slug": "junos" }, "serial": "", diff --git a/tests/test_netbox.py b/tests/test_netbox.py index 8e208e3..92f3499 100644 --- a/tests/test_netbox.py +++ b/tests/test_netbox.py @@ -88,3 +88,32 @@ def test_inventory_include_vms_pagination(self, requests_mock, version): ) as f: expected = json.load(f) assert expected == inv.dict() + + @pytest.mark.parametrize("version", ["2.8.9"]) + def test_inventory_use_platform_slug(self, requests_mock, version): + inv = get_inv( + requests_mock, self.plugin, False, version, use_platform_slug=True + ) + with open( + f"{BASE_PATH}/{self.plugin.__name__}/{version}/expected_use_platform_slug.json", + "r", + ) as f: + expected = json.load(f) + assert expected == inv.dict() + + @pytest.mark.parametrize("version", ["2.8.9"]) + def test_inventory_use_platform_slug_include_vms(self, requests_mock, version): + inv = get_inv( + requests_mock, + self.plugin, + False, + version, + use_platform_slug=True, + include_vms=True, + ) + with open( + f"{BASE_PATH}/{self.plugin.__name__}/{version}/vms-expected_use_platform_slug.json", + "r", + ) as f: + expected = json.load(f) + assert expected == inv.dict() From 3ed87794df3731cef876b39c9cd29a47bc0d1fd2 Mon Sep 17 00:00:00 2001 From: Wim Van Deun <7521270+enzzzy@users.noreply.github.com> Date: Mon, 23 Nov 2020 00:09:28 +0100 Subject: [PATCH 4/5] update README --- README.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/README.md b/README.md index d45fd0a..b1e61c3 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,39 @@ nr = InitNornir( ) ``` +### NBInventory arguments + +``` +Arguments: + nb_url: NetBox url, defaults to http://localhost:8080. + You can also use env variable NB_URL + nb_token: NetBox token. You can also use env variable NB_TOKEN + use_slugs: Whether to use slugs or not + ssl_verify: Enable/disable certificate validation or provide path to CA bundle file + flatten_custom_fields: Whether to assign custom fields directly to the host or not + filter_parameters: Key-value pairs to filter down hosts +``` + +### NetBoxInventory2 arguments + +``` +Environment Variables: + * ``NB_URL``: Corresponds to nb_url argument + * ``NB_TOKEN``: Corresponds to nb_token argument +Arguments: + nb_url: NetBox url (defaults to ``http://localhost:8080``) + nb_token: NetBox API token + ssl_verify: Enable/disable certificate validation or provide path to CA bundle file + (defaults to True) + flatten_custom_fields: Assign custom fields directly to the host's data attribute + (defaults to False) + filter_parameters: Key-value pairs that allow you to filter the NetBox inventory. + include_vms: Get virtual machines from NetBox as well as devices. + (defaults to False) + use_platform_slug: Use the NetBox platform slug for the platform attribute of a Host + (defaults to False) +``` + # Useful Links - [Nornir](https://github.com/nornir-automation/nornir) From f393eefecea40d1119b271a7eb5d75cb45ea87f1 Mon Sep 17 00:00:00 2001 From: Wim Van Deun <7521270+enzzzy@users.noreply.github.com> Date: Tue, 24 Nov 2020 23:36:37 +0100 Subject: [PATCH 5/5] prepare 0.2.0 release --- CHANGELOG.md | 7 +++++++ pyproject.toml | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..cf254e8 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,7 @@ +# Changelog + +## 0.2.0 - November 24 2020 + +* implements support for virtual-machines in NetBoxInventory2 [@patrickdaj](https://github.com/patrickdaj) +* fix github workflow deprecating set-env, add-path commands +* implement `use_platform_slug` for NetBoxInventory2 diff --git a/pyproject.toml b/pyproject.toml index 3fff575..29c6ded 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "nornir_netbox" -version = "0.1.1" +version = "0.2.0" description = "Netbox plugin for Nornir" authors = ["Wim Van Deun <7521270+enzzzy@users.noreply.github.com>", "Clay Curtis <2114016+clay584@users.noreply.github.com>"] license = "Apache-2.0"