Skip to content
This repository has been archived by the owner on Oct 25, 2023. It is now read-only.

Commit

Permalink
Merge pull request #47 from networktocode-llc/network_containers
Browse files Browse the repository at this point in the history
Add support for Network Containers in RFC1918.
  • Loading branch information
smk4664 authored Dec 30, 2021
2 parents f5f4253 + ea8e943 commit 9b729ec
Show file tree
Hide file tree
Showing 11 changed files with 299 additions and 29 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ PLUGINS_CONFIG = {
"NAUTOBOT_INFOBLOX_PASSWORD": os.getenv("NAUTOBOT_INFOBLOX_PASSWORD", ""),
"NAUTOBOT_INFOBLOX_VERIFY_SSL": os.getenv("NAUTOBOT_INFOBLOX_VERIFY_SSL", "true"),
"NAUTOBOT_INFOBLOX_WAPI_VERSION": os.getenv("NAUTOBOT_INFOBLOX_WAPI_VERSION", "v2.12"),
"enable_sync_to_infoblox": False,
"enable_rfc1918_network_containers": False,
}
}
```
Expand Down
1 change: 1 addition & 0 deletions development/nautobot_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@
"NAUTOBOT_INFOBLOX_VERIFY_SSL": os.getenv("NAUTOBOT_INFOBLOX_VERIFY_SSL", "true"),
"NAUTOBOT_INFOBLOX_WAPI_VERSION": os.getenv("NAUTOBOT_INFOBLOX_WAPI_VERSION", "v2.12"),
"enable_sync_to_infoblox": False,
"enable_rfc1918_network_containers": True,
},
"nautobot_ssot": {
"hide_example_jobs": True,
Expand Down
1 change: 1 addition & 0 deletions nautobot_ssot_infoblox/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class NautobotSSoTInfobloxConfig(PluginConfig):
max_version = "1.9999"
default_settings = {
"enable_sync_to_infoblox": False,
"enable_rfc1918_network_containers": False,
}
caching_config = {}

Expand Down
34 changes: 34 additions & 0 deletions nautobot_ssot_infoblox/diffsync/adapters/infoblox.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
"""Infoblox Adapter for Infoblox integration with SSoT plugin."""
import ipaddress

from diffsync import DiffSync
from nautobot_ssot_infoblox.diffsync.client import InfobloxApi
from nautobot_ssot_infoblox.diffsync.models.infoblox import (
InfobloxAggregate,
InfobloxIPAddress,
InfobloxNetwork,
InfobloxVLANView,
Expand Down Expand Up @@ -80,3 +83,34 @@ def load(self):
self.load_ipaddresses()
self.load_vlanviews()
self.load_vlans()


class InfobloxAggregateAdapter(DiffSync):
"""DiffSync adapter using requests to communicate to Infoblox server."""

aggregate = InfobloxAggregate

top_level = ["aggregate"]

def __init__(self, *args, job=None, sync=None, **kwargs):
"""Initialize Infoblox.
Args:
job (object, optional): Infoblox job. Defaults to None.
sync (object, optional): Infoblox DiffSync. Defaults to None.
"""
super().__init__(*args, **kwargs)
self.job = job
self.sync = sync
self.conn = InfobloxApi()

def load(self):
"""Method for loading aggregate models."""
for container in self.conn.get_network_containers():
network = ipaddress.ip_network(container["network"])
if network.is_private:
new_aggregate = self.aggregate(
network=container["network"],
description=container["comment"] if container.get("comment") else "",
)
self.add(new_aggregate)
38 changes: 36 additions & 2 deletions nautobot_ssot_infoblox/diffsync/adapters/nautobot.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
"""Nautobot Adapter for Infoblox integration with SSoT plugin."""
import re
from diffsync import DiffSync
from nautobot.ipam.models import IPAddress, Prefix, VLAN, VLANGroup
from nautobot_ssot_infoblox.diffsync.models import NautobotNetwork, NautobotIPAddress, NautobotVlanGroup, NautobotVlan
from nautobot.ipam.models import Aggregate, IPAddress, Prefix, VLAN, VLANGroup
from nautobot_ssot_infoblox.diffsync.models import (
NautobotAggregate,
NautobotNetwork,
NautobotIPAddress,
NautobotVlanGroup,
NautobotVlan,
)
from nautobot_ssot_infoblox.utils import nautobot_vlan_status


Expand Down Expand Up @@ -77,3 +83,31 @@ def load(self):
self.load_ipaddresses()
self.load_vlangroups()
self.load_vlans()


class NautobotAggregateAdapter(DiffSync):
"""DiffSync adapter using ORM to communicate to Nautobot Aggregrates."""

aggregate = NautobotAggregate

top_level = ["aggregate"]

def __init__(self, *args, job=None, sync=None, **kwargs):
"""Initialize Nautobot.
Args:
job (object, optional): Nautobot job. Defaults to None.
sync (object, optional): Nautobot DiffSync. Defaults to None.
"""
super().__init__(*args, **kwargs)
self.job = job
self.sync = sync

def load(self):
"""Method to load aggregate models from Nautobot."""
for aggregate in Aggregate.objects.all():
_aggregate = self.aggregate(
network=str(aggregate.prefix),
description=aggregate.description,
)
self.add(_aggregate)
128 changes: 105 additions & 23 deletions nautobot_ssot_infoblox/diffsync/client.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""All interactions with infoblox."""
"""All interactions with infoblox.""" # pylint: disable=too-many-lines

import copy
import json
Expand Down Expand Up @@ -120,6 +120,22 @@ def _get_network_ref(self, prefix): # pylint: disable=inconsistent-return-state
if item["network"] == prefix:
return item["_ref"]

def _get_network_container_ref(self, prefix): # pylint: disable=inconsistent-return-statements
"""Fetch the _ref of a networkcontainer resource.
Args:
prefix (str): IPv4 Prefix to fetch the _ref for.
Returns:
(str) networkcontainer _ref or None
Returns Response:
"networkcontainer/ZG5zLm5ldHdvcmtfY29udGFpbmVyJDE5Mi4xNjguMi4wLzI0LzA:192.168.2.0/24/default"
"""
for item in self.get_network_containers():
if item["network"] == prefix:
return item["_ref"]

def get_all_ipv4address_networks(self, prefix):
"""Gets all used / unused IPv4 addresses within the supplied network.
Expand Down Expand Up @@ -233,7 +249,7 @@ def get_all_networks(self, prefix=None):
logger.info(response.json)
return response.json().get("result")

def create_network(self, prefix):
def create_network(self, prefix, comment=None):
"""Create a network.
Args:
Expand All @@ -245,7 +261,7 @@ def create_network(self, prefix):
Return Response:
"network/ZG5zLm5ldHdvcmskMTkyLjE2OC4wLjAvMjMvMA:192.168.0.0/23/default"
"""
params = {"network": prefix}
params = {"network": prefix, "comment": comment}
api_path = "network"
response = self._request("POST", api_path, params=params)
logger.info(response.text)
Expand Down Expand Up @@ -298,6 +314,71 @@ def update_network(self, prefix, comment=None):
logger.info(response)
return response

def create_network_container(self, prefix, comment=None):
"""Create a network container.
Args:
prefix (str): IP network to create.
Returns:
(str) of reference network
Return Response:
"networkcontainer/ZG5zLm5ldHdvcmskMTkyLjE2OC4wLjAvMjMvMA:192.168.0.0/23/default"
"""
params = {"network": prefix, "comment": comment}
api_path = "networkcontainer"
response = self._request("POST", api_path, params=params)
logger.info(response.text)
return response.text

def delete_network_container(self, prefix):
"""Delete a network container.
Args:
prefix (str): IPv4 prefix to delete.
Returns:
(dict) deleted prefix.
Returns Response:
{"deleted": "networkcontainer/ZG5zLm5ldHdvcmskMTkyLjAuMi4wLzI0LzA:192.0.2.0/24/default"}
"""
resource = self._get_network_container_ref(prefix)

if resource:
self._delete(resource)
response = {"deleted": resource}
else:
response = {"error": f"{prefix} not found."}

logger.info(response)
return response

def update_network_container(self, prefix, comment=None):
"""Update a network container.
Args:
(str): IPv4 prefix to update.
comment (str): IPv4 prefix update comment.
Returns:
(dict) updated prefix.
Return Response:
{"updated": "networkcontainer/ZG5zLm5ldHdvcmskMTkyLjE2OC4wLjAvMjMvMA:192.168.0.0/23/default"}
"""
resource = self._get_network_container_ref(prefix)

if resource:
params = {"network": prefix, "comment": comment}
self._update(resource, **params)
response = {"updated": resource}
else:
response = {"error": f"error updating {prefix}"}
logger.info(response)
return response

def get_host_record_by_name(self, fqdn):
"""Gets the host record by using FQDN.
Expand Down Expand Up @@ -633,26 +714,6 @@ def get_authoritative_zone(self):
"fqdn": "test-site",
"view": "default"
},
{
"_ref": "zone_auth/ZG5zLnpvbmUkLl9kZWZhdWx0LmNvbS5uZXR3b3JrdG9jb2Rl:networktocode.com/default",
"fqdn": "networktocode.com",
"view": "default"
},
{
"_ref": "zone_auth/ZG5zLnpvbmUkLl9kZWZhdWx0LmNvbS5uZXR3b3JrdG9jb2RlLm5ldHdvcms:network.networktocode.com/default",
"fqdn": "network.networktocode.com",
"view": "default"
},
{
"_ref": "zone_auth/ZG5zLnpvbmUkLl9kZWZhdWx0LmNvbS5uZXR3b3JrdG9jb2RlLm5ldHdvcmsudGVzdC1zaXRl:test-site.network.networktocode.com/default",
"fqdn": "test-site.network.networktocode.com",
"view": "default"
},
{
"_ref": "zone_auth/ZG5zLnpvbmUkLl9kZWZhdWx0LmFycGEuaW4tYWRkci4xMA:10.0.0.0%2F8/default",
"fqdn": "10.0.0.0/8",
"view": "default"
}
]
"""
url_path = "zone_auth"
Expand Down Expand Up @@ -992,3 +1053,24 @@ def update_ipaddress(self, ip_address, **data): # pylint: disable=inconsistent-
response = self._request("PUT", path=resource, params=params, json=data)
logger.info("Infoblox IP Address updated: %s", response.json())
return response.json()

def get_network_containers(self):
"""Get all Network Containers.
Returns:
(list) of record dicts
Return Response:
[
{
"_ref": "networkcontainer/ZG5zLm5ldHdvcmtfY29udGFpbmVyJDE5Mi4xNjguMi4wLzI0LzA:192.168.2.0/24/default",
"network": "192.168.2.0/24",
"network_view": "default"
}
]
"""
url_path = "networkcontainer"
params = {"_return_as_object": 1, "_return_fields": "network,comment,network_view"}
response = self._request("GET", url_path, params=params)
logger.info(response.json)
return response.json().get("result")
6 changes: 4 additions & 2 deletions nautobot_ssot_infoblox/diffsync/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
"""Initialize models for Nautobot and Infoblox."""
from .nautobot import NautobotNetwork, NautobotIPAddress, NautobotVlanGroup, NautobotVlan
from .infoblox import InfobloxNetwork, InfobloxIPAddress, InfobloxVLANView, InfobloxVLAN
from .nautobot import NautobotAggregate, NautobotNetwork, NautobotIPAddress, NautobotVlanGroup, NautobotVlan
from .infoblox import InfobloxAggregate, InfobloxNetwork, InfobloxIPAddress, InfobloxVLANView, InfobloxVLAN


__all__ = [
"NautobotAggregate",
"NautobotNetwork",
"NautobotIPAddress",
"NautobotVlanGroup",
"NautobotVlan",
"InfobloxAggregate",
"InfobloxNetwork",
"InfobloxIPAddress",
"InfobloxVLANView",
Expand Down
11 changes: 11 additions & 0 deletions nautobot_ssot_infoblox/diffsync/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,14 @@ class IPAddress(DiffSyncModel):
prefix: str
status: str
description: Optional[str]


class Aggregate(DiffSyncModel):
"""Aggregate model for DiffSync."""

_modelname = "aggregate"
_identifiers = ("network",)
_attributes = ("description",)

network: str
description: Optional[str]
26 changes: 25 additions & 1 deletion nautobot_ssot_infoblox/diffsync/models/infoblox.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""Infoblox Models for Infoblox integration with SSoT plugin."""
from nautobot_ssot_infoblox.diffsync.models.base import Network, IPAddress, Vlan, VlanView
from nautobot_ssot_infoblox.diffsync.models.base import Aggregate, Network, IPAddress, Vlan, VlanView


class InfobloxNetwork(Network):
Expand Down Expand Up @@ -59,3 +59,27 @@ def update(self, attrs):
json = {"comment": attrs["description"]}
self.diffsync.conn.update_ipaddress(address=self.get_identifiers()["address"], data=json)
return super().update(attrs)


class InfobloxAggregate(Aggregate):
"""Infoblox implementation of the Aggregate Model."""

@classmethod
def create(cls, diffsync, ids, attrs):
"""Create Network Container object in Infoblox."""
diffsync.conn.create_network_container(
prefix=ids["network"], comment=attrs["description"] if attrs.get("description") else ""
)
return super().create(ids=ids, diffsync=diffsync, attrs=attrs)

def update(self, attrs):
"""Update Network Container object in Infoblox."""
self.diffsync.conn.update_network_container(
prefix=self.get_identifiers()["network"], comment=attrs["description"] if attrs.get("description") else ""
)
return super().update(attrs)

def delete(self):
"""Delete Network Container object in Infoblox."""
self.diffsync.conn.delete_network_container(self.get_identifiers()["network"])
return super().delete()
Loading

0 comments on commit 9b729ec

Please sign in to comment.