Boost gendoc with an ActionPlugin instead of a module
parent
2be4377eea
commit
0c0fcadd8e
action_plugins
|
@ -0,0 +1,181 @@
|
|||
#!/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
|
||||
|
||||
result['changed'] = False
|
||||
|
||||
|
||||
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)
|
||||
|
||||
changed = False
|
||||
|
||||
try:
|
||||
raw = self.craft_request("?action=raw")(url, cookie)
|
||||
if raw != content:
|
||||
changed = True
|
||||
except urllib.error.HTTPError: # We will create the page.
|
||||
changed = True
|
||||
raw = ""
|
||||
|
||||
# Display any change
|
||||
if changed:
|
||||
diff = difflib.unified_diff(raw.splitlines(), content.splitlines(), fromfile="old", tofile="new", lineterm="")
|
||||
for line in diff:
|
||||
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)
|
||||
|
||||
|
||||
self._supports_check_mode = True
|
||||
self._supports_async = False
|
||||
|
||||
return result
|
Loading…
Reference in New Issue