Compare commits

..

7 Commits

Author SHA1 Message Date
4626ec797d Prepare 0.3.0
All checks were successful
continuous-integration/drone/tag Build is passing
2023-04-13 07:30:07 +02:00
94e94037aa Fill the README with samples 2023-04-13 07:29:42 +02:00
75a3f6a718 New module service 2023-04-13 06:58:11 +02:00
aad2797d84 New module domain 2023-04-13 06:58:00 +02:00
03652187ac New module provider 2023-04-13 06:57:52 +02:00
fae6d90d76 New module user 2023-04-13 06:57:48 +02:00
9714665c4f Also install pip modules 2023-04-13 04:41:37 +02:00
8 changed files with 471 additions and 2 deletions

113
README.md
View File

@ -27,7 +27,118 @@ collections:
## Using this collection
TODO
### Deploy happyDomain
To setup happyDomain as a local service (support Docker, openrc and systemd), use the role `happydns.happydomain.happydomain`:
```yaml
roles:
- name: happydns.happydomain.happydomain
use_container: no # yes if you want to use Docker instead
```
### Create a user account on your happyDomain instance
```yaml
tasks:
- happydns.happydomain.user:
username: frederic@happydomain.org
password: "mySuperS3cur3P4$$w0rd"
```
This will create and enabled the user (no need to validate the email).
### Register a NS provider
Eg. for an AXFR/DDNS provider:
```yaml
tasks:
- happydns.happydomain.provider:
name: test
type: DDNSServer
data:
server: 192.168.0.42
keyname: ddns
algorithm: hmac-sha256
keyblob: yourBASE64Secret==
happydomain_username: frederic@happydomain.org
happydomain_password: "mySuperS3cur3P4$$w0rd"
```
### Handle a new domain name in happyDomain
```yaml
tasks:
- happydns.happydomain.domain:
provider: test
domain: happydomain.tf
happydomain_username: frederic@happydomain.org
happydomain_password: "mySuperS3cur3P4$$w0rd"
```
### Create a new record for a domain
First, you need a zoneid:
```yaml
tasks:
- happydns.happydomain.domain:
provider: test
domain: happydomain.tf
happydomain_username: frederic@happydomain.org
happydomain_password: "mySuperS3cur3P4$$w0rd"
register: my_zone
```
Note the `register`ed variable.
Then, use the `happydns.happydomain.service` module:
```yaml
tasks:
- happydns.happydomain.service:
happydomain_username: frederic@happydomain.org
happydomain_password: "mySuperS3cur3P4$$w0rd"
domain: happydomain.tf
zone: "{{ my_zone.current_zone }}"
subdomain: "test"
type: abstract.Server
service:
A: 127.0.0.1
AAAA: "::1"
apply_changes: yes
```
This will add two records under `test.happydomain.tf`: A and AAAA (part of `abstract.Server`).
### Remove a given record
You'll also need a zoneid, see previous section. Then:
```yaml
tasks:
- happydns.happydomain.service:
happydomain_username: frederic@happydomain.org
happydomain_password: "mySuperS3cur3P4$$w0rd"
domain: happydomain.tf
zone: "{{ my_zone.current_zone }}"
subdomain: "test"
type: scvs.TXT
service:
content: "This is a test record"
state: absent
apply_changes: yes
```
This will remove all records matching:
```
test IN TXT "This is a test record"
```
## Code of Conduct

View File

@ -1,7 +1,7 @@
---
namespace: happydns
name: happydomain
version: 0.2.3
version: 0.3.0
readme: README.md
authors:
- happyDomain Team <happydomain.org>

View File

91
plugins/modules/domain.py Normal file
View File

@ -0,0 +1,91 @@
# -*- coding: utf-8 -*-
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = '''
---
'''
from ansible.module_utils.basic import AnsibleModule
from happydomain.api import HappyDomain
def main():
module = AnsibleModule(
argument_spec=dict(
state=dict(type='str', default='present', choices=['absent', 'present']),
happydomain_username=dict(type='str', aliases=['email']),
happydomain_password=dict(type='str', aliases=['passwd'], no_log=True),
happydomain_token=dict(type='str'),
happydomain_scheme=dict(type='str', default='http'),
happydomain_host=dict(type='str', default='localhost'),
happydomain_port=dict(type='int', default='8081'),
happydomain_baseurl=dict(type='str', default=''),
provider=dict(type='str'),
domain=dict(type='str', aliases=['name']),
import_zone=dict(type='bool', default=False),
)
)
p = module.params
result = {
"changed": False
}
found = False
a = HappyDomain(
scheme=p['happydomain_scheme'],
host=p['happydomain_host'],
port=p['happydomain_port'],
baseurl=p['happydomain_baseurl'],
token=p['happydomain_token'],
)
if p['happydomain_password'] is not None:
a.login(p['happydomain_username'], p['happydomain_password'])
domains = a.domain_list()
for d in domains:
if d.domain == p['domain'] or d.domain == p['domain'] + ".":
found = True
if p['state'] == 'absent':
d.delete()
result['changed'] = True
result['msg'] = "domain " + p['domain'] + " deleted"
elif len(d.zone_history) == 0 and p['import_zone']:
result['changed'] = True
result['current_zone'] = d.current_zone.id
result['msg'] += " and zone imported"
else:
result['current_zone'] = d.current_zone
break
if not found:
providers = a.provider_list()
provider_found = False
for s in providers:
if s._comment == p['provider']:
provider_found = True
dn = s.domain_add(p['domain'])
result['msg'] = "domain " + p['domain'] + " added"
if p['import_zone']:
result['current_zone'] = dn.current_zone.id
result['msg'] += " and zone imported"
if not provider_found:
module.fail_json(msg="No provider found with name " + p['provider'])
return
result['changed'] = True
module.exit_json(**result)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,79 @@
# -*- coding: utf-8 -*-
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = '''
---
'''
from ansible.module_utils.basic import AnsibleModule
from happydomain.api import HappyDomain
def main():
module = AnsibleModule(
argument_spec=dict(
state=dict(type='str', default='present', choices=['absent', 'present']),
happydomain_username=dict(type='str', aliases=['email']),
happydomain_password=dict(type='str', aliases=['passwd'], no_log=True),
happydomain_token=dict(type='str'),
happydomain_scheme=dict(type='str', default='http'),
happydomain_host=dict(type='str', default='localhost'),
happydomain_port=dict(type='int', default='8081'),
happydomain_baseurl=dict(type='str', default=''),
type=dict(type='str'),
name=dict(type='str', aliases=['comment']),
data=dict(type='dict'),
)
)
p = module.params
changed = False
found = False
a = HappyDomain(
scheme=p['happydomain_scheme'],
host=p['happydomain_host'],
port=p['happydomain_port'],
baseurl=p['happydomain_baseurl'],
token=p['happydomain_token'],
)
if p['happydomain_password'] is not None:
a.login(p['happydomain_username'], p['happydomain_password'])
providers = a.provider_list()
for s in providers:
if s._srctype == p['type'] and s._comment == p['name']:
found = True
if p['state'] == 'absent':
s.delete()
changed = True
else:
for k in p['data']:
if k not in s.args or p['data'][k] != s.args[k]:
s.args = p['data']
changed = True
break
if changed:
s.update()
break
if not found and p['state'] != 'absent':
a.provider_add(p['type'], p['name'], p['data'])
changed = True
module.exit_json(
changed=changed,
msg="provider " + p['name'] + ((" created" if not found else " altered") if p['state'] != 'absent' else " deleted"),
)
if __name__ == "__main__":
main()

109
plugins/modules/service.py Normal file
View File

@ -0,0 +1,109 @@
# -*- coding: utf-8 -*-
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = '''
---
'''
from ansible.module_utils.basic import AnsibleModule
from happydomain.api import HappyDomain
def main():
module = AnsibleModule(
argument_spec=dict(
state=dict(type='str', default='present', choices=['absent', 'present']),
happydomain_username=dict(type='str', aliases=['email']),
happydomain_password=dict(type='str', aliases=['passwd'], no_log=True),
happydomain_token=dict(type='str'),
happydomain_scheme=dict(type='str', default='http'),
happydomain_host=dict(type='str', default='localhost'),
happydomain_port=dict(type='int', default='8081'),
happydomain_baseurl=dict(type='str', default=''),
domain=dict(type='str', aliases=['name']),
zone=dict(type='str'),
type=dict(type='str'),
subdomain=dict(type='str'),
service=dict(type='dict'),
erase_others=dict(type='bool'),
apply_changes=dict(type='bool'),
)
)
p = module.params
result = {
"changed": False
}
a = HappyDomain(
scheme=p['happydomain_scheme'],
host=p['happydomain_host'],
port=p['happydomain_port'],
baseurl=p['happydomain_baseurl'],
token=p['happydomain_token'],
)
if p['happydomain_password'] is not None:
a.login(p['happydomain_username'], p['happydomain_password'])
domains = a.domain_list()
for d in domains:
if d.domain == p['domain'] or d.domain == p['domain'] + ".":
for z in d.zone_history:
if z == p['zone']:
zone = d.get_zone(z)
if p['subdomain'].removesuffix(d.domain) in zone.services:
for s in zone.services[p['subdomain'].removesuffix(d.domain)]:
if s._svctype == p['type']:
differ = False
for k in s.service:
if k not in p['service'] or s.service[k] != p['service'][k]:
differ = True
break
if p['erase_others']:
if differ:
s.delete()
result['changed'] = True
elif not differ:
if p['state'] == 'absent':
s.delete()
result['changed'] = True
else:
module.exit_json(**result)
return
if p['state'] != 'absent':
zone.add_zone_service(
p['subdomain'].removesuffix(d.domain),
p['type'],
p['service'],
)
result['changed'] = True
if p['apply_changes']:
zone.apply_changes()
result['published'] = True
result['changed'] = True
break
else:
module.fail_json(msg="No zone found with id " + p['zone'])
return
break
else:
module.fail_json(msg="No domain found with name " + p['domain'])
return
module.exit_json(**result)
if __name__ == "__main__":
main()

73
plugins/modules/user.py Executable file
View File

@ -0,0 +1,73 @@
# -*- coding: utf-8 -*-
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = '''
---
'''
from ansible.module_utils.basic import AnsibleModule
from happydomain.admin import Admin
def main():
module = AnsibleModule(
argument_spec=dict(
state=dict(type='str', default='present', choices=['absent', 'present']),
username=dict(type='str', aliases=['name', 'email', 'happydomain_username']),
password=dict(type='str', aliases=['passwd', 'happydomain_password'], no_log=True),
allowcommercials=dict(type='bool'),
email_verified=dict(type='bool', default=True),
socket=dict(type='path', default='/var/lib/happydomain/happydomain.sock'),
)
)
p = module.params
changed = False
found = False
a = Admin(socket=p['socket'])
users = a.authuser_list()
for u in users:
if u.Email == p['username']:
found = True
if p['state'] == 'absent':
u.Delete()
changed = True
else:
changed = u.ResetPassword(p['password'])
changedProp = False
if u.AllowCommercials != p['allowcommercials']:
u.AllowCommercials = p['allowcommercials']
changedProp = True
if u.EmailVerification is None and p['email_verified']:
now = datetime.now()
now = now.replace(microsecond=0)
u.EmailVerification = now.isoformat()
changedProp = True
if changedProp:
u.Update()
changed = True
break
if not found and p['state'] != 'absent':
a.authuser_create(p['username'], p['password'], p['allowcommercials'], p['email_verified'])
changed = True
module.exit_json(
changed=changed,
msg="user " + p['username'] + ((" created" if not found else " altered") if p['state'] != 'absent' else " deleted"),
)
if __name__ == "__main__":
main()

View File

@ -77,3 +77,9 @@
enabled: yes
state: started
when: ansible_service_mgr == "systemd"
- name: Install happydomain python package
ansible.builtin.pip:
name:
- happydomain
- requests_unixsocket