diff --git a/action_plugins/moinmoin_page.py b/action_plugins/moinmoin_page.py new file mode 100755 index 0000000000000000000000000000000000000000..30bd8c4975eb1d95a18b825767fdc544d3127221 --- /dev/null +++ b/action_plugins/moinmoin_page.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python3 + +# Copyright: (c) 2019, Alexandre Iooss <erdnaxe@crans.org> +# +# GNU General Public License v3.0+ + +import re +import urllib.error +import urllib.parse +import urllib.request +import difflib + + +from ansible.errors import AnsibleError +from ansible.plugins.action import ActionBase +from ansible.utils.display import Display +from ansible.module_utils._text import to_native + +display = Display() + + +class ActionModule(ActionBase): + + TRANSFERS_FILES = False + _VALID_ARGS = frozenset(('url', 'user', 'password', 'content', 'revision_comment')) + + def login(self, url, user, password): + """ + Log in and return session cookie or None if failed + + :param url: random wiki url (not root page) + :param user: wiki user + :param password: user's password + :return: session cookie + """ + # Send a HTTP POST request + data = urllib.parse.urlencode({ + 'action': 'login', + 'login': 'Connexion', + 'name': user, + 'password': password + }).encode() + req = urllib.request.Request(url, data) + try: + response = urllib.request.urlopen(req) + cookie = response.getheader('set-cookie') + except urllib.error.HTTPError as e: + # If 404, then also return header + cookie = e.getheader('set-cookie') + + # Check that authentication worked + if not cookie: + raise AnsibleError(to_native('server did not return a session cookie')) + return cookie + + def craft_request(self, suffix): + """ + Crafts a function that takes an url and a cookie, + and returns the content of the requested page with given action suffix. + """ + def f(url, cookie): + req = urllib.request.Request(url + suffix) + req.add_header("Cookie", cookie) + content = urllib.request.urlopen(req).read().decode('utf-8') + return content + return f + + + def edit_ticket(self, url, cookie): + """ + Return edition ticket of url + + :param url: page to edit + :param cookie: session cookie + :return: edit ticket + """ + # Send request with session cookie + content = self.craft_request("?action=edit&editor=text")(url, cookie) + + # Search for ticket + search = re.search('name=\"ticket\" value=\"([^\"]*)\"', content) + if not search: + raise AnsibleError(to_native('no edit ticket was found')) + + return search.group(1) + + + def edit(self, url, user, password, content, revision_comment, cookie): + """ + Edit a MoinMoin wiki page + + :param url: page to edit + :param user: wiki user + :param password: user's password + :param content: content to place on this page + :param revision_comment: revision comment + """ + # Connect and get edit ticket + ticket = self.edit_ticket(url, cookie) + + # Create request and send + data = { + 'button_save': 'Enregistrer les modifications', + 'category': '', + 'comment': revision_comment.encode("utf-8"), + 'savetext': content.encode("utf-8"), + 'action': 'edit', + 'ticket': ticket + } + req = urllib.request.Request(url, urllib.parse.urlencode(data).encode()) + req.add_header("Cookie", cookie) + urllib.request.urlopen(req) + + + def run(self, tmp=None, task_vars=None): + """ + The run method is the main Action Plugin driver. All work is done from within this method. + + tmp: Temporary directory. Sometimes an action plugin sets up + a temporary directory and then calls another module. This parameter + allows us to reuse the same directory for both. + + task_vars: The variables (host vars, group vars, config vars, etc) associated with this task. + Note that while this will contain Ansible facts from the host, they should be used + with caution as a user running Ansible can disable their collection. If you want + make sure that your Action Plugin always has access to the ones it needs, you may + want to consider running the setup module directly in the run the method and getting + the Ansible facts that way. + The strategy plugin which manages running tasks on instances uses an ansible.vars.manager + VariableManager instance to retrieve this context specific dict of variables. + """ + if task_vars is None: + task_vars = dict() + + + result = super(ActionModule, self).run(tmp, task_vars) + del tmp + + + url = self._task.args.get("url") + user = self._task.args.get("user") + password = self._task.args.get("password") + content = self._task.args.get("content") + revision_comment = self._task.args.get("revision_comment") + + cookie = self.login(url, user, password) + + try: + raw = self.craft_request("?action=raw")(url, cookie) + except urllib.error.HTTPError: # We will create the page. + raw = "" + + diff = difflib.unified_diff(raw.splitlines(), content.splitlines(), fromfile="old", tofile="new", lineterm="") + i=0 + + # Display any change + for line in diff: + i+=1 + if line.startswith("-"): + display.display(line, "red") + elif line.startswith("+"): + display.display(line, "green") + elif line.startswith("@"): + display.display(line, "yellow") + else: + display.display(line) + + # Do apply the change if not in check mode + if not self._play_context.check_mode: + self.edit(url, user, password, content, revision_comment, cookie) + + result['changed']=i>0 + + self._supports_check_mode = True + self._supports_async = False + + return result diff --git a/all.yml b/all.yml index 709ba117d65e758e97df9ec12d746853ab7c4512..67830dafb264ce0a3c8275690f81603d93cdd8c9 100755 --- a/all.yml +++ b/all.yml @@ -5,13 +5,13 @@ - import_playbook: plays/mail.yml - import_playbook: plays/nfs.yml -#- import_playbook: plays/logs.yml -#- import_playbook: plays/backup.yml -- import_playbook: plays/network-interfaces.yml +#- import_playbook: plays/logs.yml TODO: rsyncd +- import_playbook: plays/backup.yml +# - import_playbook: plays/network-interfaces.yml TODO: check this paybook - import_playbook: plays/monitoring.yml # Services that only apply to a subset of server -- import_playbook: plays/cas.yml +# - import_playbook: plays/cas.yml - import_playbook: plays/dhcp.yml - import_playbook: plays/dns.yml - import_playbook: plays/etherpad.yml diff --git a/hosts b/hosts index ed6d9d842be8afcfaef893f464f2298c5b8914ca..a87d64d941832666d8b2f336040a0c12ec8d1f67 100644 --- a/hosts +++ b/hosts @@ -1,70 +1,59 @@ # Crans servers inventory -# How to name your server ? -# > We name servers according to location, then type. -# > Then we regroup everything in global geographic and type groups. - - -# [framadate] -# voyager.adm.crans.org -# -# [dhcp] -# dhcp.adm.crans.org -# odlyd.adm.crans.org -# -# [keepalived] -# gulp.adm.crans.org -# odlyd.adm.crans.org -# eap.adm.crans.org -# radius.adm.crans.org -# frontdaur.adm.crans.org -# bakdaur.adm.crans.org -# -# [test_vm] -# re2o-test.adm.crans.org -[dovecot] -owl.adm.crans.org - [backups] zephir.adm.crans.org -[certbot] -gitzly.adm.crans.org +[baie] +cameron.adm.crans.org +tealc.adm.crans.org + +[bdd] +tealc.adm.crans.org [certbot:children] +dovecot +git radius # We use certbot to manage LE certificates reverseproxy -dovecot -[nginx_rtmp] -fluxx.adm.crans.org +[dhcp:children] +routeurs_vm -[reverseproxy] -hodaur.adm.crans.org +[dns_auth_master] +silice.adm.crans.org -[roundcube] -roundcube-srv.adm.crans.org +[dns_authoritative:children] +dns_auth_master +freebox +ovh_physical + +[dns_recursive:children] +routeurs_vm + +[dovecot] +owl.adm.crans.org [ethercalc] ethercalc-srv.adm.crans.org -[horde] -horde.adm.crans.org +[framadate] +voyager.adm.crans.org -[radius] -routeur-sam.adm.crans.org +[freebox] +boeing.adm.crans.org +titanic.adm.crans.org -[re2o] -re2o-newinfra.adm.crans.org -routeur-sam.adm.crans.org +[git] +gitzly.adm.crans.org -[bdd] -tealc.adm.crans.org +[horde] +horde.adm.crans.org -[virtu] -sam.adm.crans.org -daniel.adm.crans.org -jack.adm.crans.org +[irc] +irc.adm.crans.org + +[keepalived:children] +routeurs_vm [ldap_server] tealc.adm.crans.org @@ -72,55 +61,98 @@ sam.adm.crans.org daniel.adm.crans.org jack.adm.crans.org -[keepalived] -routeur-sam.adm.crans.org -#routeur-daniel.adm.crans.org +[monitoring] +monitoring.adm.crans.org + +[nginx] +charybde.adm.crans.org + +[nginx_rtmp] +fluxx.adm.crans.org + +[nginx:children] +reverseproxy + +[postfix] +mailman.adm.crans.org +redisdead.adm.crans.org +zamok.adm.crans.org + +[postfix:children] +freebox +ovh_physical + +[radius:children] +routeurs_vm + +[re2o] +re2o-newinfra.adm.crans.org + +[re2o:children] +radius -[dhcp] +[reverseproxy] +hodaur.adm.crans.org + +[roundcube] +roundcube-srv.adm.crans.org + +[routeurs_vm] +routeur-daniel.adm.crans.org +routeur-jack.adm.crans.org routeur-sam.adm.crans.org -#routeur-daniel.adm.crans.org + +[virtu] +daniel.adm.crans.org +jack.adm.crans.org +sam.adm.crans.org [crans_routeurs:children] -dhcp -keepalived +# dhcp TODO: Really needed ? +# keepalived +routeurs_vm [crans_physical] -cameron.adm.crans.org -tealc.adm.crans.org -sam.adm.crans.org -daniel.adm.crans.org -jack.adm.crans.org -#gulp.adm.crans.org -zephir.adm.crans.org +charybde.adm.crans.org +omnomnom.adm.crans.org +zamok.adm.crans.org + +[crans_physical:children] +backups +baie +virtu [crans_vm] -owl.adm.crans.org -codichotomie.adm.crans.org -voyager.adm.crans.org -#silice.adm.crans.org -routeur-sam.adm.crans.org -#routeur-daniel.adm.crans.org #belenios.adm.crans.org -#re2o-ldap.adm.crans.org +bigbluebutton.adm.crans.org +#boeing.adm.crans.org +#casouley.adm.crans.org +codichotomie.adm.crans.org +#ethercalc-srv.adm.crans.org +fluxx.adm.crans.org gitlab-ci.adm.crans.org gitzly.adm.crans.org hodaur.adm.crans.org -monitoring.adm.crans.org -#boeing.adm.crans.org -fluxx.adm.crans.org -#unifi.adm.crans.org -#pastemoisa.adm.crans.org -#casouley.adm.crans.org -kiwi.adm.crans.org -kiwijuice.adm.crans.org -tracker.adm.crans.org +horde.adm.crans.org +irc.adm.crans.org jitsi.adm.crans.org -#ethercalc-srv.adm.crans.org kenobi.adm.crans.org -roundcube.adm.crans.org -horde.adm.crans.org -bigbluebutton.adm.crans.org +kiwi.adm.crans.org +kiwijuice.adm.crans.org +monitoring.adm.crans.org +owl.adm.crans.org owncloud.adm.crans.org +#re2o-ldap.adm.crans.org +redisdead.adm.crans.org +roundcube.adm.crans.org +#silice.adm.crans.org +titanic.adm.crans.org +tracker.adm.crans.org +voyager.adm.crans.org +#unifi.adm.crans.org + +[crans_vm:children] +routeurs_vm [ovh_physical] sputnik.adm.crans.org @@ -129,7 +161,6 @@ sputnik.adm.crans.org [crans_server:children] crans_physical crans_vm -crans_routeurs # everything at crans [crans:children] @@ -147,7 +178,6 @@ ovh_physical # every virtual machine [vm:children] crans_vm -crans_routeurs # every server [server:children] diff --git a/plays/dns.yml b/plays/dns.yml index b261acaa7293ba4dc0ace4eb27b24e0231c9acce..4e61330fbcfc8e2174ab732e058e89602b78189f 100755 --- a/plays/dns.yml +++ b/plays/dns.yml @@ -1,12 +1,12 @@ #!/usr/bin/env ansible-playbook --- # Deploy recursive DNS cache server -- hosts: routeur-sam.adm.crans.org,routeur-daniel.adm.crans.org +- hosts: dns_recursive roles: - bind-recursive # Deploy authoritative DNS server -- hosts: silice.adm.crans.org,sputnik.adm.crans.org,boeing.adm.crans.org +- hosts: dns_authoritative vars: certbot_dns_secret: "{{ vault_certbot_dns_secret }}" certbot_adm_dns_secret: "{{ vault_certbot_adm_dns_secret }}" @@ -18,7 +18,7 @@ roles: - bind-authoritative -- hosts: silice.adm.crans.org +- hosts: dns_auth_master vars: re2o: server: re2o.adm.crans.org diff --git a/plays/mail.yml b/plays/mail.yml index ea4cc641b3234fbc1711fba625c6f4544873f4a2..536a2b6866fb3a4cfb4b639d6c21b6377fe56b1a 100755 --- a/plays/mail.yml +++ b/plays/mail.yml @@ -6,7 +6,7 @@ # All other servers uses nullmailer to send local mail to Crans SMTP. # Redirect local mail to mailserver -- hosts: crans_server,!redisdead.adm.crans.org,!soyouz.adm.crans.org,!titanic.adm.crans.org,!boeing.adm.crans.org,!sputnik.adm.crans.org,!zamok.adm.crans.org,!mailman.adm.crans.org +- hosts: crans_server,!postfix vars: mail_root: root@crans.org mail_smtp_server: smtp.adm.crans.org diff --git a/plays/monitoring.yml b/plays/monitoring.yml index ca8fc85a006bf8beff366f0fcbb71d43b438247f..2a6c6bcd8d03d78d714c34a6162948d2ed8822f2 100755 --- a/plays/monitoring.yml +++ b/plays/monitoring.yml @@ -1,7 +1,7 @@ #!/usr/bin/env ansible-playbook --- # Deploy Prometheus and Grafana on monitoring server -- hosts: monitoring.adm.crans.org +- hosts: monitoring vars: # Prometheus targets.json prometheus: @@ -64,13 +64,13 @@ # Monitor all hosts -- hosts: server,test_vm +- hosts: server vars: adm_ipv4: "{{ query('ldap', 'ip', ansible_hostname, 'adm') | ipv4 | first }}" roles: ["prometheus-node-exporter"] # Export nginx metrics -- hosts: charybde.adm.crans.org,hodaur.adm.crans.org +- hosts: nginx vars: adm_ipv4: "{{ query('ldap', 'ip', ansible_hostname, 'adm') | ipv4 | first }}" roles: ["prometheus-nginx-exporter"] diff --git a/plays/root.yml b/plays/root.yml index a6b8f7906abeb8b1a4f469aa8071b684b41b6e8c..73e17b54114ec3cd52ae129ef8fd1e61db2db5a0 100755 --- a/plays/root.yml +++ b/plays/root.yml @@ -20,9 +20,7 @@ insertafter: '127.0.0.1 localhost' when: check_mirror.found == 0 - - -- hosts: tealc.adm.crans.org +- hosts: baie roles: - baie @@ -75,7 +73,7 @@ roles: - openssh -- hosts: server +- hosts: crans_vm tasks: - name: Remove cloud-init apt: diff --git a/roles/moinmoin-gendoc/library/dmidecode.py b/roles/moinmoin-gendoc/library/dmidecode_facts.py similarity index 70% rename from roles/moinmoin-gendoc/library/dmidecode.py rename to roles/moinmoin-gendoc/library/dmidecode_facts.py index 6e01acc36f5a78424bdb93a610dc48bf86913054..765713d6149841c5c68a3321abfd315379113fa9 100644 --- a/roles/moinmoin-gendoc/library/dmidecode.py +++ b/roles/moinmoin-gendoc/library/dmidecode_facts.py @@ -32,7 +32,6 @@ EXAMPLES = ''' ''' -import dmidecode import json from ansible.module_utils.basic import AnsibleModule @@ -48,16 +47,30 @@ def decode_dict(data): def run_module(): module = AnsibleModule( - argument_spec = {} + argument_spec = {}, + supports_check_mode=True, ) - dmi_data = decode_dict({ - 'bios': dmidecode.bios(), - 'processor': dmidecode.processor(), - 'system': dmidecode.system(), - 'memory': dmidecode.memory(), - 'slot': dmidecode.slot(), - }) - module.exit_json(changed=True, ansible_facts=dmi_data) + + try: + import dmidecode + dmi_data = decode_dict({ + 'bios': dmidecode.bios(), + 'processor': dmidecode.processor(), + 'system': dmidecode.system(), + 'memory': dmidecode.memory(), + 'slot': dmidecode.slot(), + }) + + except ImportError: + dmi_data = { + 'bios': dict(), + 'processor': dict(), + 'system': dict(), + 'memory': dict(), + 'slot': dict(), + } + + module.exit_json(changed=False, ansible_facts=dmi_data) def main(): diff --git a/roles/moinmoin-gendoc/library/moinmoin_page.py b/roles/moinmoin-gendoc/library/moinmoin_page.py deleted file mode 100644 index b6f6ee91a23af165afd08f7ac01099d4f59c3563..0000000000000000000000000000000000000000 --- a/roles/moinmoin-gendoc/library/moinmoin_page.py +++ /dev/null @@ -1,139 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright: (c) 2019, Alexandre Iooss <erdnaxe@crans.org> -# GNU General Public License v3.0+ - -""" -This module simulate the edition of a MoinMoin wiki page - -Example: - moinmoin_page: - url: https://wiki.crans.org/WikiErdnaxe - user: WikiErdnaxe - password: HoTuNeMeConnaisPas - content: "{{ lookup('template', 'mapage.j2') }}" - revision_comment: Bip bip -""" - -import re -import urllib.error -import urllib.parse -import urllib.request - -from ansible.module_utils.basic import AnsibleModule - - -def login(url, user, password): - """ - Log in and return session cookie or None if failed - - :param url: random wiki url (not root page) - :param user: wiki user - :param password: user's password - :return: session cookie - """ - # Send a HTTP POST request - data = urllib.parse.urlencode({ - 'action': 'login', - 'login': 'Connexion', - 'name': user, - 'password': password - }).encode() - req = urllib.request.Request(url, data) - try: - response = urllib.request.urlopen(req) - cookie = response.getheader('set-cookie') - except urllib.error.HTTPError as e: - # If 404, then also return header - cookie = e.getheader('set-cookie') - - # Check that authentication worked - assert cookie, 'server did not return a session cookie' - return cookie - - -def edit_ticket(url, cookie): - """ - Return edition ticket of url - - :param url: page to edit - :param cookie: session cookie - :return: edit ticket - """ - # Send request with session cookie - suffix = "?action=edit&editor=text" - req = urllib.request.Request(url + suffix) - req.add_header("Cookie", cookie) - content = urllib.request.urlopen(req).read().decode('utf-8') - - # Search for ticket - search = re.search('name=\"ticket\" value=\"([^\"]*)\"', content) - assert search, 'no edit ticket was found' - return search.group(1) - - -def edit(url, user, password, content, revision_comment): - """ - Edit a MoinMoin wiki page - - :param url: page to edit - :param user: wiki user - :param password: user's password - :param content: content to place on this page - :param revision_comment: revision comment - """ - # Connect and get edit ticket - cookie = login(url, user, password) - ticket = edit_ticket(url, cookie) - - # Create request and send - data = { - 'button_save': 'Enregistrer les modifications', - 'category': '', - 'comment': revision_comment.encode("utf-8"), - 'savetext': content.encode("utf-8"), - 'action': 'edit', - 'ticket': ticket - } - req = urllib.request.Request(url, urllib.parse.urlencode(data).encode()) - req.add_header("Cookie", cookie) - urllib.request.urlopen(req) - - -def run_module(): - # Define arguments that should be passed - module_args = { - 'url': {'type': 'str', 'required': True}, - 'user': {'type': 'str', 'required': True}, - 'password': {'type': 'str', 'required': True}, - 'content': {'type': 'str', 'required': True}, - 'revision_comment': {'type': 'str', 'required': True}, - } - - # Define arguments that are returned - result = { - 'changed': False, - } - - # Our AnsibleModule - module = AnsibleModule( - argument_spec=module_args, - supports_check_mode=True - ) - - # TODO: get current wiki page and compare - result['changed'] = True - - # If not is check mode and page need to change, then update page - if not module.check_mode and result['changed']: - edit(**module.params) - - module.exit_json(**result) - - -def main(): - run_module() - - -if __name__ == '__main__': - main() diff --git a/roles/moinmoin-gendoc/tasks/main.yml b/roles/moinmoin-gendoc/tasks/main.yml index bcc819b5d1e403cdfcbc5ef56f7de166300d215d..a821e24791dc731454bda34376ecde627503de13 100644 --- a/roles/moinmoin-gendoc/tasks/main.yml +++ b/roles/moinmoin-gendoc/tasks/main.yml @@ -8,7 +8,7 @@ until: apt_result is succeeded - name: get dmidecode facts - dmidecode: {} + dmidecode_facts: {} - name: get ssh fingerprints sshfp: {} diff --git a/roles/moinmoin-gendoc/templates/server.j2 b/roles/moinmoin-gendoc/templates/server.j2 index e287e258f246eef8d6943296fcb24346694d0f9b..c49a503996995abfecc3f67cd972bf8580508106 100644 --- a/roles/moinmoin-gendoc/templates/server.j2 +++ b/roles/moinmoin-gendoc/templates/server.j2 @@ -4,7 +4,7 @@ == Caractéristiques matérielles == {% if ansible_form_factor != 'Other' and ansible_form_factor != 'Unknown' %} -'''Forme du serveur''' : +'''Forme du serveur''' : {{ ansible_form_factor }} {% endif %} @@ -56,7 +56,7 @@ et {{ (ansible_memory_mb.swap.total/1024)|round(1) }} GiB de SWAP. == Caractéristiques logicielles == '''Système d'exploitation''' : -{{ ansible_lsb.description }} +{{ ansible_distribution }} {{ ansible_distribution_major_version }} ({{ ansible_distribution_release }}) '''Noyau''' : {{ ansible_kernel }}