Skip to content

Commit e5d53f2

Browse files
committed
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
1 parent b8b1ddd commit e5d53f2

File tree

10 files changed

+591
-0
lines changed

10 files changed

+591
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ A last option to read the docs is the docs folder in this repository.
4646
The following dependencies have to be fulfiled by the Ansible controller.
4747

4848
* colour
49+
* geopy
4950
* inflection
5051
* ipaddress
5152
* phpypam>=1.0.0
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
minor_changes:
2+
- add `location_module` to `create`, `update` and `delete` locations
3+
this module also implement a facility to resolve the location from a address or a lat/lon pair

docs/plugins/index.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Modules
2323
* :ref:`device <ansible_collections.codeaffen.phpipam.device_module>` -- Manage devices
2424
* :ref:`device_type <ansible_collections.codeaffen.phpipam.device_type_module>` -- Manage device types
2525
* :ref:`domain <ansible_collections.codeaffen.phpipam.domain_module>` -- Manage L2 routing domains
26+
* :ref:`location <ansible_collections.codeaffen.phpipam.location_module>` -- Manage locations
2627
* :ref:`nameserver <ansible_collections.codeaffen.phpipam.nameserver_module>` -- Manage nameservers
2728
* :ref:`section <ansible_collections.codeaffen.phpipam.section_module>` -- Manage sections
2829
* :ref:`subnet <ansible_collections.codeaffen.phpipam.subnet_module>` -- Manage subnets
@@ -44,6 +45,7 @@ Modules
4445
device_module
4546
device_type_module
4647
domain_module
48+
location_module
4749
nameserver_module
4850
section_module
4951
subnet_module

docs/plugins/location_module.rst

Lines changed: 360 additions & 0 deletions
Large diffs are not rendered by default.

plugins/modules/location.py

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
#!/usr/bin/env python
2+
3+
# -*- coding: utf-8 -*-
4+
# (c) Christian Meißner 2021
5+
#
6+
# This program is free software: you can redistribute it and/or modify
7+
# it under the terms of the GNU General Public License as published by
8+
# the Free Software Foundation, either version 3 of the License, or
9+
# (at your option) any later version.
10+
#
11+
# This program is distributed in the hope that it will be useful,
12+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
# GNU General Public License for more details.
15+
#
16+
# You should have received a copy of the GNU General Public License
17+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
19+
from __future__ import absolute_import, division, print_function
20+
__metaclass__ = type
21+
22+
DOCUMENTATION = '''
23+
---
24+
module: location
25+
version_added: 1.4.0
26+
short_description: Manage locations
27+
description:
28+
- create, update and delete locations
29+
author:
30+
- "Christian Meißner (@cmeissner)"
31+
options:
32+
name:
33+
description: Name of the given location
34+
type: str
35+
required: true
36+
description:
37+
description: Description of the given location
38+
type: str
39+
required: false
40+
address:
41+
description:
42+
- Address of the given location
43+
- if I(resolv_location) is set to True, this address will be used to resolve the latitude and longitude
44+
- Mutually exclusive with latitude and longitude
45+
type: str
46+
required: false
47+
latitude:
48+
description:
49+
- Latitude of the given location
50+
- if I(resolv_location) is set to True, this latitude will be used to resolve the address
51+
- This parameter is mutually exclusive with address
52+
- This parameter is required if I(longitude) is given
53+
type: float
54+
required: false
55+
longitude:
56+
description:
57+
- Longitude of the given location
58+
- if I(resolv_location) is set to True, this longitude will be used to resolve the address
59+
- This parameter is mutually exclusive with address
60+
- This parameter is required if I(latitude) is given
61+
type: float
62+
required: false
63+
resolv_location:
64+
description:
65+
- Resolve the given location
66+
- Requires network connectivity to https://nominatim.org/ to resolve the given address
67+
- If address can not be resolved, latitude and longitude will not be set
68+
type: bool
69+
required: false
70+
default: no
71+
requirements:
72+
- geopy
73+
extends_documentation_fragment:
74+
- codeaffen.phpipam.phpipam
75+
- codeaffen.phpipam.phpipam.entity_state
76+
'''
77+
78+
EXAMPLES = '''
79+
- name: "Create with address"
80+
codeaffen.phpipam.location:
81+
username: "admin"
82+
password: "s3cr3t"
83+
server_url: "https://ipam.example.com"
84+
name: "my location"
85+
description: "my location description"
86+
address: "my location address"
87+
state: present
88+
89+
- name: "Create location with geo coordinates"
90+
codeaffen.phpipam.location:
91+
username: "admin"
92+
password: "s3cr3t"
93+
server_url: "https://ipam.example.com"
94+
name: "my location"
95+
description: "my location description"
96+
latitude: 123.456
97+
longitude: 123.456
98+
state: present
99+
100+
- name: "Remove location"
101+
codeaffen.phpipam.location:
102+
username: "admin"
103+
password: "s3cr3t"
104+
server_url: "https://ipam.example.com"
105+
name: "my location"
106+
state: absent
107+
'''
108+
109+
import traceback
110+
from ansible_collections.codeaffen.phpipam.plugins.module_utils.phpipam_helper import PhpipamEntityAnsibleModule
111+
112+
try:
113+
from geopy.geocoders import Nominatim
114+
from geopy.point import Point
115+
HAS_GEOPY = True
116+
except ImportError:
117+
HAS_GEOPY = False
118+
GEOPY_IMP_ERR = traceback.format_exc()
119+
120+
121+
class PhpipamToolsLocationsModule(PhpipamEntityAnsibleModule):
122+
pass
123+
124+
125+
def main():
126+
module = PhpipamToolsLocationsModule(
127+
phpipam_spec=dict(
128+
id=dict(type='int', invisible=True, phpipam_name='id'),
129+
name=dict(type='str', required=True),
130+
description=dict(type='str', required=False),
131+
address=dict(type='str', required=False),
132+
latitude=dict(type='float', required=False, phpipam_name='lat'),
133+
longitude=dict(type='float', required=False, phpipam_name='long'),
134+
resolv_location=dict(type='bool', required=False, default=False, api_invisible=True),
135+
),
136+
mutually_exclusive=[['address', 'latitude'], ['address', 'longitude']],
137+
required_together=[['latitude', 'longitude']],
138+
)
139+
140+
module_params = module.phpipam_params
141+
142+
if module_params['resolv_location']:
143+
if not HAS_GEOPY:
144+
module.fail_json(msg='geopy is required for resolv_location', exception=GEOPY_IMP_ERR)
145+
146+
geolocator = Nominatim(user_agent='ansible-phpipam-module')
147+
if 'address' in module_params and module_params['resolv_location']:
148+
location = geolocator.geocode(module_params['address'])
149+
if location:
150+
module_params['latitude'] = str(location.latitude)
151+
module_params['longitude'] = str(location.longitude)
152+
elif 'latitude' in module_params and 'longitude' in module_params:
153+
location = geolocator.reverse(Point(float(module_params['latitude']), float(module_params['longitude'])))
154+
if location:
155+
module_params['address'] = location.address
156+
module_params['latitude'] = str(location.latitude)
157+
module_params['longitude'] = str(location.longitude)
158+
159+
with module.api_connection():
160+
module.run()
161+
162+
163+
if __name__ == "__main__":
164+
main()

requirements-dev.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
ansible
22
colour
3+
geopy
34
wheel
45
jinja2 # pyup: ignore
56
PyYAML~=5.3

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
colour
2+
geopy
23
inflection
34
ipaddress
45
phpypam

tests/test_playbooks/location.yml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
---
2+
- hosts: localhost
3+
collections:
4+
- codeaffen.phpipam
5+
gather_facts: false
6+
vars_files:
7+
- vars/server.yml
8+
- vars/location.yml
9+
tasks:
10+
- name: create location
11+
include: tasks/location.yml
12+
vars:
13+
name: create location
14+
location: "{{ base_location_data }}"
15+
16+
- name: create location again, no change
17+
include: tasks/location.yml
18+
vars:
19+
name: create location again, no change
20+
location: "{{ base_location_data }}"
21+
22+
- name: update location
23+
include: tasks/location.yml
24+
vars:
25+
name: update location
26+
override:
27+
address: "Alexander Platz, Berlin, Germany"
28+
latitude: "{{ omit }}"
29+
longitude: "{{ omit }}"
30+
location: "{{ base_location_data | combine(override) }}"
31+
32+
- name: delete location
33+
include: tasks/location.yml
34+
vars:
35+
type: delete location
36+
override:
37+
state: absent
38+
location: "{{ base_location_data | combine(override) }}"
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
- name: "Ensure state of location: {{ name }}"
3+
location:
4+
server_url: "{{ phpipam_server_url }}"
5+
app_id: "{{ phpipam_app_id }}"
6+
username: "{{ phpipam_username }}"
7+
password: "{{ phpipam_password }}"
8+
name: "{{ location.name }}"
9+
description: "{{ location.description | default(omit) }}"
10+
address: "{{ location.address | default(omit) }}"
11+
latitude: "{{ location.latitude | default(omit) }}"
12+
longitude: "{{ location.longitude | default(omit) }}"
13+
resolv_location: "{{ location.resolv_location | default(omit) }}"
14+
state: "{{ location.state | default('present') }}"
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
base_location_data:
3+
name: ansible_test_location
4+
description: test location
5+
latitude: 52.5319638
6+
longitude: 13.3925654
7+
resolv_location: Yes

0 commit comments

Comments
 (0)