Getting closer to where I want to be
This commit is contained in:
parent
b601dc270b
commit
e287ab5526
|
|
@ -3,22 +3,21 @@
|
||||||
# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
|
# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
from __future__ import (absolute_import, division, print_function)
|
from __future__ import (absolute_import, division, print_function)
|
||||||
|
|
||||||
__metaclass__ = type
|
__metaclass__ = type
|
||||||
|
|
||||||
DOCUMENTATION = r"""
|
DOCUMENTATION = r'''
|
||||||
name: netbird
|
name: netbird
|
||||||
author: Mark J. Horninger (@spam-n-eggs)
|
author: Mark J. Horninger (@spam-n-eggs)
|
||||||
version_added: "0.0.2"
|
version_added: 0.0.2
|
||||||
requirements:
|
requirements:
|
||||||
- requests>=2.31.0
|
- requests>=2.31.0
|
||||||
short_description: Get inventory from the Netbird API
|
short_description: Get inventory from the Netbird API
|
||||||
description:
|
description:
|
||||||
- Get inventory from the Netbird API. Allows for filtering based on Netbird Tags / Groups.
|
- Get inventory from the Netbird API. Allows for filtering based on Netbird Tags / Groups.
|
||||||
extends_documentation_fragment:
|
extends_documentation_fragment:
|
||||||
- constructed
|
- constructed
|
||||||
- inventory_cache
|
- inventory_cache
|
||||||
options:
|
options:
|
||||||
cache:
|
cache:
|
||||||
description: Cache plugin output to a file
|
description: Cache plugin output to a file
|
||||||
type: boolean
|
type: boolean
|
||||||
|
|
@ -28,22 +27,22 @@ DOCUMENTATION = r"""
|
||||||
required: true
|
required: true
|
||||||
choices: ['netbird', 'dominion_solutions.netbird']
|
choices: ['netbird', 'dominion_solutions.netbird']
|
||||||
api_key:
|
api_key:
|
||||||
description: The API Key for the Netbird API.
|
description: The API Key for the Netbird API.
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
env:
|
env:
|
||||||
- name: NETBIRD_API_KEY
|
- name: NETBIRD_API_KEY
|
||||||
api_url:
|
api_url:
|
||||||
description: The URL for the Netbird API.
|
description: The URL for the Netbird API.
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
env:
|
env:
|
||||||
- name: NETBIRD_API_URL
|
- name: NETBIRD_API_URL
|
||||||
disconnected:
|
include_disconnected:
|
||||||
description: Whether or not to include disconnected peers in the inventory
|
description: Whether or not to include disconnected peers in the inventory
|
||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
default: false
|
||||||
"""
|
'''
|
||||||
|
|
||||||
EXAMPLES = r"""
|
EXAMPLES = r"""
|
||||||
"""
|
"""
|
||||||
|
|
@ -51,9 +50,11 @@ EXAMPLES = r"""
|
||||||
from ansible.errors import AnsibleError
|
from ansible.errors import AnsibleError
|
||||||
|
|
||||||
from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable
|
from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable
|
||||||
|
from ansible.utils.display import Display
|
||||||
|
|
||||||
# Specific for the NetbirdAPI Class
|
# Specific for the NetbirdAPI Class
|
||||||
import json
|
import json
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import requests
|
import requests
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
@ -61,13 +62,19 @@ except ImportError:
|
||||||
else:
|
else:
|
||||||
HAS_NETBIRD_API_LIBS = True
|
HAS_NETBIRD_API_LIBS = True
|
||||||
|
|
||||||
|
display = Display()
|
||||||
|
|
||||||
|
|
||||||
class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||||
NAME = "dominion_solutions.netbird"
|
NAME = "dominion_solutions.netbird"
|
||||||
|
|
||||||
|
_redirected_names = ["netbird", "dominion_solutions.netbird"]
|
||||||
|
|
||||||
|
_load_name = NAME
|
||||||
|
|
||||||
def _build_client(self, loader):
|
def _build_client(self, loader):
|
||||||
"""Build the Netbird API Client"""
|
"""Build the Netbird API Client"""
|
||||||
|
display.v("Building the Netbird API Client.")
|
||||||
api_key = self.get_option('api_key')
|
api_key = self.get_option('api_key')
|
||||||
api_url = self.get_option('api_url')
|
api_url = self.get_option('api_url')
|
||||||
if self.templar.is_template(api_key):
|
if self.templar.is_template(api_key):
|
||||||
|
|
@ -80,13 +87,17 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||||
if api_url is None:
|
if api_url is None:
|
||||||
raise AnsibleError("Could not retrieve the Netbird API URL from the configuration sources.")
|
raise AnsibleError("Could not retrieve the Netbird API URL from the configuration sources.")
|
||||||
|
|
||||||
|
display.v(f"Set up the Netbird API Client with the URL: {api_url}")
|
||||||
self.client = NetbirdApi(api_key, api_url)
|
self.client = NetbirdApi(api_key, api_url)
|
||||||
|
|
||||||
def _get_peer_inventory(self):
|
def _get_peer_inventory(self):
|
||||||
"""Get the inventory from the Netbird API"""
|
"""Get the inventory from the Netbird API"""
|
||||||
if self.disconnected is False:
|
self.display.v("Getting the inventory from the Netbird API.")
|
||||||
|
if self.include_disconnected is False:
|
||||||
|
self.display.vv("Filtering out disconnected peers.")
|
||||||
self.peers = [peer for peer in self.client.ListPeers() if peer.data["connected"] is True]
|
self.peers = [peer for peer in self.client.ListPeers() if peer.data["connected"] is True]
|
||||||
else:
|
else:
|
||||||
|
display.vv("Including disconnected peers.")
|
||||||
self.peers = self.client.ListPeers()
|
self.peers = self.client.ListPeers()
|
||||||
|
|
||||||
def _filter_by_config(self):
|
def _filter_by_config(self):
|
||||||
|
|
@ -98,16 +109,23 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||||
if any(group in peer.groups for group in groups)
|
if any(group in peer.groups for group in groups)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def verify_file(self, path):
|
||||||
|
"""Verify the Linode configuration file."""
|
||||||
|
if super(InventoryModule, self).verify_file(path):
|
||||||
|
endings = ('netbird.yaml', 'netbird.yml')
|
||||||
|
if any((path.endswith(ending) for ending in endings)):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def parse(self, inventory, loader, path, cache=True):
|
def parse(self, inventory, loader, path, cache=True):
|
||||||
"""Dynamically parse the inventory from the Netbird API"""
|
"""Dynamically parse the inventory from the Netbird API"""
|
||||||
super(InventoryModule, self).parse(inventory, loader, path)
|
super(InventoryModule, self).parse(inventory, loader, path)
|
||||||
if not HAS_NETBIRD_API_LIBS:
|
if not HAS_NETBIRD_API_LIBS:
|
||||||
raise AnsibleError("the Netbird Dynamic inventory requires Requests.")
|
raise AnsibleError("the Netbird Dynamic inventory requires Requests.")
|
||||||
|
|
||||||
|
self._options = self._read_config_data(path)
|
||||||
self.peers = None
|
self.peers = None
|
||||||
self.disconnected = self.get_option('disconnected')
|
|
||||||
|
|
||||||
self._read_config_data(path)
|
|
||||||
cache_key = self.get_cache_key(path)
|
cache_key = self.get_cache_key(path)
|
||||||
|
|
||||||
if cache:
|
if cache:
|
||||||
|
|
@ -122,6 +140,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||||
# Check for None rather than False in order to allow
|
# Check for None rather than False in order to allow
|
||||||
# for empty sets of cached instances
|
# for empty sets of cached instances
|
||||||
if self.peers is None:
|
if self.peers is None:
|
||||||
|
self.include_disconnected = self.get_option('include_disconnected')
|
||||||
self._build_client(loader)
|
self._build_client(loader)
|
||||||
self._get_peer_inventory()
|
self._get_peer_inventory()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ ansible>=9.2.0
|
||||||
ansible-core>=2.16.3
|
ansible-core>=2.16.3
|
||||||
cffi==1.16.0
|
cffi==1.16.0
|
||||||
cryptography==42.0.2
|
cryptography==42.0.2
|
||||||
|
epdb==0.15.1
|
||||||
Jinja2==3.1.3
|
Jinja2==3.1.3
|
||||||
MarkupSafe==2.1.5
|
MarkupSafe==2.1.5
|
||||||
packaging==23.2
|
packaging==23.2
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,16 @@ __metaclass__ = type
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
# TODO: Reenable this if needed.
|
# TODO: Reenable this if needed.
|
||||||
# import sys
|
# import sys
|
||||||
|
|
||||||
from ansible.errors import AnsibleError
|
from ansible.errors import AnsibleError
|
||||||
from ansible.parsing.dataloader import DataLoader
|
from ansible.parsing.dataloader import DataLoader
|
||||||
from ansible.template import Templar
|
from ansible.template import Templar
|
||||||
from ansible_collections.dominion_solutions.netbird.plugins.inventory.netbird import InventoryModule
|
from ansible_collections.dominion_solutions.netbird.plugins.inventory.netbird import InventoryModule, NetbirdApi, Peer
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
|
|
@ -23,6 +26,20 @@ def inventory():
|
||||||
return plugin
|
return plugin
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="module")
|
||||||
|
def netbird_api():
|
||||||
|
mock_netbird_api = NetbirdApi(None, None)
|
||||||
|
response_data = []
|
||||||
|
with open('tests/unit/module_utils/inventories/fixtures/peers.json') as peers_file:
|
||||||
|
peers_map = json.load(peers_file)
|
||||||
|
for data in peers_map:
|
||||||
|
response_data.append(Peer(data['hostname'], data['id'], data))
|
||||||
|
|
||||||
|
mock_netbird_api.ListPeers = MagicMock(return_value=response_data)
|
||||||
|
|
||||||
|
return mock_netbird_api
|
||||||
|
|
||||||
|
|
||||||
def test_missing_access_token_lookup(inventory):
|
def test_missing_access_token_lookup(inventory):
|
||||||
loader = DataLoader()
|
loader = DataLoader()
|
||||||
inventory._options = {'api_key': None, 'api_url': None}
|
inventory._options = {'api_key': None, 'api_url': None}
|
||||||
|
|
@ -39,3 +56,12 @@ def test_verify_file(tmp_path, inventory):
|
||||||
|
|
||||||
def test_verify_file_bad_config(inventory):
|
def test_verify_file_bad_config(inventory):
|
||||||
assert inventory.verify_file('foobar.netbird.yml') is False
|
assert inventory.verify_file('foobar.netbird.yml') is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_peer_data(inventory, netbird_api):
|
||||||
|
loader = DataLoader()
|
||||||
|
path = 'tests/unit/module_utils/inventories/fixtures/netbird.yml'
|
||||||
|
inventory._build_client = MagicMock()
|
||||||
|
inventory.client = netbird_api
|
||||||
|
inventory.parse(dict(), loader, path, False)
|
||||||
|
assert inventory.peers is not None
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue