Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions changelogs/fragments/location_module.yml
Original file line number Diff line number Diff line change
@@ -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
2 changes: 2 additions & 0 deletions docs/plugins/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Modules
* :ref:`device <ansible_collections.codeaffen.phpipam.device_module>` -- Manage devices
* :ref:`device_type <ansible_collections.codeaffen.phpipam.device_type_module>` -- Manage device types
* :ref:`domain <ansible_collections.codeaffen.phpipam.domain_module>` -- Manage L2 routing domains
* :ref:`location <ansible_collections.codeaffen.phpipam.location_module>` -- Manage locations
* :ref:`nameserver <ansible_collections.codeaffen.phpipam.nameserver_module>` -- Manage nameservers
* :ref:`section <ansible_collections.codeaffen.phpipam.section_module>` -- Manage sections
* :ref:`subnet <ansible_collections.codeaffen.phpipam.subnet_module>` -- Manage subnets
Expand All @@ -44,6 +45,7 @@ Modules
device_module
device_type_module
domain_module
location_module
nameserver_module
section_module
subnet_module
Expand Down
360 changes: 360 additions & 0 deletions docs/plugins/location_module.rst

Large diffs are not rendered by default.

164 changes: 164 additions & 0 deletions plugins/modules/location.py
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.

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()
1 change: 1 addition & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
ansible
colour
geopy
wheel
jinja2 # pyup: ignore
PyYAML~=5.3
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
colour
geopy
inflection
ipaddress
phpypam
38 changes: 38 additions & 0 deletions tests/test_playbooks/location.yml
Original file line number Diff line number Diff line change
@@ -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) }}"
14 changes: 14 additions & 0 deletions tests/test_playbooks/tasks/location.yml
Original file line number Diff line number Diff line change
@@ -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') }}"
7 changes: 7 additions & 0 deletions tests/test_playbooks/vars/location.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
base_location_data:
name: ansible_test_location
description: test location
latitude: 52.5319638
longitude: 13.3925654
resolv_location: Yes