From e5d53f2fe282b5f23538394668fa8868f967c753 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Mei=C3=9Fner?= Date: Mon, 13 Dec 2021 19:19:07 +0100 Subject: [PATCH] Create location module * update requirements for location module * create location module * add tests for location module * add location module documentation * add changelog fragment for location module --- README.md | 1 + changelogs/fragments/location_module.yml | 3 + docs/plugins/index.rst | 2 + docs/plugins/location_module.rst | 360 +++++++++++++++++++++++ plugins/modules/location.py | 164 +++++++++++ requirements-dev.txt | 1 + requirements.txt | 1 + tests/test_playbooks/location.yml | 38 +++ tests/test_playbooks/tasks/location.yml | 14 + tests/test_playbooks/vars/location.yml | 7 + 10 files changed, 591 insertions(+) create mode 100644 changelogs/fragments/location_module.yml create mode 100644 docs/plugins/location_module.rst create mode 100644 plugins/modules/location.py create mode 100644 tests/test_playbooks/location.yml create mode 100644 tests/test_playbooks/tasks/location.yml create mode 100644 tests/test_playbooks/vars/location.yml diff --git a/README.md b/README.md index fcaff7f..b51d402 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ A last option to read the docs is the docs folder in this repository. The following dependencies have to be fulfiled by the Ansible controller. * colour +* geopy * inflection * ipaddress * phpypam>=1.0.0 diff --git a/changelogs/fragments/location_module.yml b/changelogs/fragments/location_module.yml new file mode 100644 index 0000000..5741fa2 --- /dev/null +++ b/changelogs/fragments/location_module.yml @@ -0,0 +1,3 @@ +minor_changes: + - add `location_module` to `create`, `update` and `delete` locations + this module also implement a facility to resolve the location from a address or a lat/lon pair diff --git a/docs/plugins/index.rst b/docs/plugins/index.rst index 87b2303..d2dad98 100644 --- a/docs/plugins/index.rst +++ b/docs/plugins/index.rst @@ -23,6 +23,7 @@ Modules * :ref:`device ` -- Manage devices * :ref:`device_type ` -- Manage device types * :ref:`domain ` -- Manage L2 routing domains +* :ref:`location ` -- Manage locations * :ref:`nameserver ` -- Manage nameservers * :ref:`section ` -- Manage sections * :ref:`subnet ` -- Manage subnets @@ -44,6 +45,7 @@ Modules device_module device_type_module domain_module + location_module nameserver_module section_module subnet_module diff --git a/docs/plugins/location_module.rst b/docs/plugins/location_module.rst new file mode 100644 index 0000000..3c499a1 --- /dev/null +++ b/docs/plugins/location_module.rst @@ -0,0 +1,360 @@ +.. Document meta + +:orphan: + +.. |antsibull-internal-nbsp| unicode:: 0xA0 + :trim: + +.. role:: ansible-attribute-support-label +.. role:: ansible-attribute-support-property +.. role:: ansible-attribute-support-full +.. role:: ansible-attribute-support-partial +.. role:: ansible-attribute-support-none +.. role:: ansible-attribute-support-na + +.. Anchors + +.. _ansible_collections.codeaffen.phpipam.location_module: + +.. Anchors: short name for ansible.builtin + +.. Anchors: aliases + + + +.. Title + +codeaffen.phpipam.location -- Manage locations +++++++++++++++++++++++++++++++++++++++++++++++ + +.. Collection note + +.. note:: + This plugin is part of the `codeaffen.phpipam collection `_ (version 1.3.1). + + To install it use: :code:`ansible-galaxy collection install codeaffen.phpipam`. + + To use it in a playbook, specify: :code:`codeaffen.phpipam.location`. + +.. version_added + +.. versionadded:: 1.4.0 of codeaffen.phpipam + +.. contents:: + :local: + :depth: 1 + +.. Deprecated + + +Synopsis +-------- + +.. Description + +- create, update and delete locations + + +.. Aliases + + +.. Requirements + +Requirements +------------ +The below requirements are needed on the host that executes this module. + +- geopy +- inflection +- ipaddress +- phpypam>=1.0.0 + + +.. Options + +Parameters +---------- + +.. raw:: html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterChoices/DefaultsComments
+
+ address + +
+ string +
+
+ +
Address of the given location
+
if resolv_location is set to True, this address will be used to resolve the latitude and longitude
+
Mutually exclusive with latitude and longitude
+
+
+ app_id + +
+ string +
+
+ Default:
"ansible"
+
+
API app name
+
+
+ description + +
+ string +
+
+ +
Description of the given location
+
+
+ latitude + +
+ float +
+
+ +
Latitude of the given location
+
if resolv_location is set to True, this latitude will be used to resolve the address
+
This parameter is mutually exclusive with address
+
This parameter is required if longitude is given
+
+
+ longitude + +
+ float +
+
+ +
Longitude of the given location
+
if resolv_location is set to True, this longitude will be used to resolve the address
+
This parameter is mutually exclusive with address
+
This parameter is required if latitude is given
+
+
+ name + +
+ string + / required
+
+ +
Name of the given location
+
+
+ password + +
+ string + / required
+
+ +
Password of the user to access phpIPAM server
+
+
+ resolv_location + +
+ boolean +
+
+
    Choices: +
  • no ←
  • +
  • yes
  • +
+
+
Resolve the given location
+
Requires network connectivity to https://nominatim.org/ to resolve the given address
+
If address can not be resolved, latitude and longitude will not be set
+
+
+ server_url + +
+ string + / required
+
+ +
URL of the phpIPAM server
+
+
+ state + +
+ string +
+
+
    Choices: +
  • present ←
  • +
  • absent
  • +
+
+
State of the entity
+
+
+ username + +
+ string + / required
+
+ +
Username to access phpIPAM server
+
+
+ validate_certs + +
+ boolean +
+
+
    Choices: +
  • no
  • +
  • yes ←
  • +
+
+
Is the TLS certificate of the phpIPAM server verified or not.
+
+
+ +.. Attributes + + +.. Notes + + +.. Seealso + + +.. Examples + +Examples +-------- + +.. code-block:: yaml+jinja + + + - name: "Create with address" + codeaffen.phpipam.location: + username: "admin" + password: "s3cr3t" + server_url: "https://ipam.example.com" + name: "my location" + description: "my location description" + address: "my location address" + state: present + + - name: "Create location with geo coordinates" + codeaffen.phpipam.location: + username: "admin" + password: "s3cr3t" + server_url: "https://ipam.example.com" + name: "my location" + description: "my location description" + latitude: 123.456 + longitude: 123.456 + state: present + + - name: "Remove location" + codeaffen.phpipam.location: + username: "admin" + password: "s3cr3t" + server_url: "https://ipam.example.com" + name: "my location" + state: absent + + + + +.. Facts + + +.. Return values + + +.. Status (Presently only deprecated) + + +.. Authors + +Authors +~~~~~~~ + +- Christian Meißner (@cmeissner) + + + +.. Parsing errors + diff --git a/plugins/modules/location.py b/plugins/modules/location.py new file mode 100644 index 0000000..f6c5b3e --- /dev/null +++ b/plugins/modules/location.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python + +# -*- coding: utf-8 -*- +# (c) Christian Meißner 2021 +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +DOCUMENTATION = ''' +--- +module: location +version_added: 1.4.0 +short_description: Manage locations +description: + - create, update and delete locations +author: + - "Christian Meißner (@cmeissner)" +options: + name: + description: Name of the given location + type: str + required: true + description: + description: Description of the given location + type: str + required: false + address: + description: + - Address of the given location + - if I(resolv_location) is set to True, this address will be used to resolve the latitude and longitude + - Mutually exclusive with latitude and longitude + type: str + required: false + latitude: + description: + - Latitude of the given location + - if I(resolv_location) is set to True, this latitude will be used to resolve the address + - This parameter is mutually exclusive with address + - This parameter is required if I(longitude) is given + type: float + required: false + longitude: + description: + - Longitude of the given location + - if I(resolv_location) is set to True, this longitude will be used to resolve the address + - This parameter is mutually exclusive with address + - This parameter is required if I(latitude) is given + type: float + required: false + resolv_location: + description: + - Resolve the given location + - Requires network connectivity to https://nominatim.org/ to resolve the given address + - If address can not be resolved, latitude and longitude will not be set + type: bool + required: false + default: no +requirements: + - geopy +extends_documentation_fragment: + - codeaffen.phpipam.phpipam + - codeaffen.phpipam.phpipam.entity_state +''' + +EXAMPLES = ''' +- name: "Create with address" + codeaffen.phpipam.location: + username: "admin" + password: "s3cr3t" + server_url: "https://ipam.example.com" + name: "my location" + description: "my location description" + address: "my location address" + state: present + +- name: "Create location with geo coordinates" + codeaffen.phpipam.location: + username: "admin" + password: "s3cr3t" + server_url: "https://ipam.example.com" + name: "my location" + description: "my location description" + latitude: 123.456 + longitude: 123.456 + state: present + +- name: "Remove location" + codeaffen.phpipam.location: + username: "admin" + password: "s3cr3t" + server_url: "https://ipam.example.com" + name: "my location" + state: absent +''' + +import traceback +from ansible_collections.codeaffen.phpipam.plugins.module_utils.phpipam_helper import PhpipamEntityAnsibleModule + +try: + from geopy.geocoders import Nominatim + from geopy.point import Point + HAS_GEOPY = True +except ImportError: + HAS_GEOPY = False + GEOPY_IMP_ERR = traceback.format_exc() + + +class PhpipamToolsLocationsModule(PhpipamEntityAnsibleModule): + pass + + +def main(): + module = PhpipamToolsLocationsModule( + phpipam_spec=dict( + id=dict(type='int', invisible=True, phpipam_name='id'), + name=dict(type='str', required=True), + description=dict(type='str', required=False), + address=dict(type='str', required=False), + latitude=dict(type='float', required=False, phpipam_name='lat'), + longitude=dict(type='float', required=False, phpipam_name='long'), + resolv_location=dict(type='bool', required=False, default=False, api_invisible=True), + ), + mutually_exclusive=[['address', 'latitude'], ['address', 'longitude']], + required_together=[['latitude', 'longitude']], + ) + + module_params = module.phpipam_params + + if module_params['resolv_location']: + if not HAS_GEOPY: + module.fail_json(msg='geopy is required for resolv_location', exception=GEOPY_IMP_ERR) + + geolocator = Nominatim(user_agent='ansible-phpipam-module') + if 'address' in module_params and module_params['resolv_location']: + location = geolocator.geocode(module_params['address']) + if location: + module_params['latitude'] = str(location.latitude) + module_params['longitude'] = str(location.longitude) + elif 'latitude' in module_params and 'longitude' in module_params: + location = geolocator.reverse(Point(float(module_params['latitude']), float(module_params['longitude']))) + if location: + module_params['address'] = location.address + module_params['latitude'] = str(location.latitude) + module_params['longitude'] = str(location.longitude) + + with module.api_connection(): + module.run() + + +if __name__ == "__main__": + main() diff --git a/requirements-dev.txt b/requirements-dev.txt index 9584568..1922bc7 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,5 +1,6 @@ ansible colour +geopy wheel jinja2 # pyup: ignore PyYAML~=5.3 diff --git a/requirements.txt b/requirements.txt index 008871e..947ee6a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ colour +geopy inflection ipaddress phpypam diff --git a/tests/test_playbooks/location.yml b/tests/test_playbooks/location.yml new file mode 100644 index 0000000..55e4e4e --- /dev/null +++ b/tests/test_playbooks/location.yml @@ -0,0 +1,38 @@ +--- +- hosts: localhost + collections: + - codeaffen.phpipam + gather_facts: false + vars_files: + - vars/server.yml + - vars/location.yml + tasks: + - name: create location + include: tasks/location.yml + vars: + name: create location + location: "{{ base_location_data }}" + + - name: create location again, no change + include: tasks/location.yml + vars: + name: create location again, no change + location: "{{ base_location_data }}" + + - name: update location + include: tasks/location.yml + vars: + name: update location + override: + address: "Alexander Platz, Berlin, Germany" + latitude: "{{ omit }}" + longitude: "{{ omit }}" + location: "{{ base_location_data | combine(override) }}" + + - name: delete location + include: tasks/location.yml + vars: + type: delete location + override: + state: absent + location: "{{ base_location_data | combine(override) }}" diff --git a/tests/test_playbooks/tasks/location.yml b/tests/test_playbooks/tasks/location.yml new file mode 100644 index 0000000..95454e7 --- /dev/null +++ b/tests/test_playbooks/tasks/location.yml @@ -0,0 +1,14 @@ +--- +- name: "Ensure state of location: {{ name }}" + location: + server_url: "{{ phpipam_server_url }}" + app_id: "{{ phpipam_app_id }}" + username: "{{ phpipam_username }}" + password: "{{ phpipam_password }}" + name: "{{ location.name }}" + description: "{{ location.description | default(omit) }}" + address: "{{ location.address | default(omit) }}" + latitude: "{{ location.latitude | default(omit) }}" + longitude: "{{ location.longitude | default(omit) }}" + resolv_location: "{{ location.resolv_location | default(omit) }}" + state: "{{ location.state | default('present') }}" diff --git a/tests/test_playbooks/vars/location.yml b/tests/test_playbooks/vars/location.yml new file mode 100644 index 0000000..d9d7a2a --- /dev/null +++ b/tests/test_playbooks/vars/location.yml @@ -0,0 +1,7 @@ +--- +base_location_data: + name: ansible_test_location + description: test location + latitude: 52.5319638 + longitude: 13.3925654 + resolv_location: Yes