mirror of
git://git.yoctoproject.org/yocto-autobuilder2.git
synced 2025-07-05 05:04:47 +02:00
211 lines
6.6 KiB
Python
211 lines
6.6 KiB
Python
#
|
|
# SPDX-License-Identifier: GPL-2.0-only
|
|
#
|
|
|
|
'''
|
|
Created on Dec 13, 2016
|
|
|
|
__author__ = "Joshua Lock"
|
|
__copyright__ = "Copyright 2016, Intel Corp."
|
|
__credits__ = ["Joshua Lock"]
|
|
'''
|
|
|
|
import codecs
|
|
import hashlib
|
|
import time
|
|
import requests
|
|
from twisted.python import log
|
|
|
|
|
|
class YPWiki(object):
|
|
MAX_TRIES = 5
|
|
TIMEOUT = 60
|
|
|
|
def __init__(self, wiki_uri, wiki_un, wiki_pass):
|
|
self.wiki_uri = wiki_uri
|
|
self.wiki_un = wiki_un
|
|
self.wiki_pass = wiki_pass
|
|
|
|
@staticmethod
|
|
def retry_request(requesturl, **kwargs):
|
|
"""
|
|
Rather than failing when a request to a 'requesturl' throws an
|
|
exception retry again a minute later. Perform this retry no more than
|
|
5 times.
|
|
|
|
@type requesturl: string
|
|
"""
|
|
kwargs['timeout'] = YPWiki.TIMEOUT
|
|
|
|
def try_request():
|
|
try:
|
|
req = requests.get(requesturl, **kwargs)
|
|
return req
|
|
except (requests.exceptions.RequestException,
|
|
requests.exceptions.Timeout):
|
|
return None
|
|
|
|
tries = 0
|
|
req = None
|
|
while not req and tries < YPWiki.MAX_TRIES:
|
|
if tries > 0:
|
|
time.sleep(60)
|
|
req = try_request()
|
|
tries = tries + 1
|
|
|
|
return req
|
|
|
|
@staticmethod
|
|
def parse_json(response):
|
|
"""
|
|
This method handles stripping UTF-8 BOM from the beginning of responses
|
|
from the Yocto Project wiki.
|
|
|
|
http://en.wikipedia.org/wiki/Byte_Order_Mark
|
|
http://bugs.python.org/issue18958
|
|
|
|
@type response: requests.Response
|
|
"""
|
|
bom = codecs.BOM_UTF8
|
|
text = ''
|
|
|
|
# In Requests 0.8.2 (Ubuntu 12.04) Response.content has type unicode,
|
|
# whereas in requests 2.1.10 (Fedora 23) Response.content is a str
|
|
# Ensure that bom is the same type as the content, codecs.BOM_UTF8 is
|
|
# a str
|
|
|
|
# If we discover a BOM set the encoding appropriately so that the
|
|
# built in decoding routines in requests work correctly.
|
|
if response.content.startswith(bom):
|
|
response.encoding = 'utf-8-sig'
|
|
|
|
return response.json()
|
|
|
|
def login(self):
|
|
"""
|
|
Login to the wiki and return cookies for the logged in session
|
|
"""
|
|
payload = {
|
|
'action': 'login',
|
|
'lgname': self.wiki_un,
|
|
'lgpassword': self.wiki_pass,
|
|
'utf8': '',
|
|
'format': 'json'
|
|
}
|
|
|
|
try:
|
|
req1 = requests.post(self.wiki_uri, data=payload,
|
|
timeout=self.TIMEOUT)
|
|
except (requests.exceptions.RequestException,
|
|
requests.exceptions.Timeout):
|
|
return None
|
|
|
|
parsed = self.parse_json(req1)
|
|
login_token = parsed['login']['token'].encode('utf-8')
|
|
|
|
payload['lgtoken'] = login_token
|
|
try:
|
|
req2 = requests.post(self.wiki_uri, data=payload,
|
|
cookies=req1.cookies, timeout=self.TIMEOUT)
|
|
except (requests.exceptions.RequestException,
|
|
requests.exceptions.Timeout):
|
|
return None
|
|
|
|
return req2.cookies.copy()
|
|
|
|
def get_content(self, wiki_page):
|
|
"""
|
|
Get the current content of the 'wiki_page' -- to make the wiki page
|
|
as useful as possible the most recent log entry should be at the top,
|
|
to that end we need to edit the whole page so that we can insert the
|
|
new entry after the log but before the other entries.
|
|
|
|
This method fetches the current page content, splits out the blurb and
|
|
returns a pair:
|
|
1) the blurb
|
|
2) the current entries
|
|
|
|
@type wiki_page: string
|
|
"""
|
|
|
|
pm = '?format=json&action=query&prop=revisions&rvprop=content&titles='
|
|
|
|
req = self.retry_request(self.wiki_uri+pm+wiki_page)
|
|
if not req:
|
|
return None, None
|
|
|
|
parsed = self.parse_json(req)
|
|
pageid = sorted(parsed['query']['pages'].keys())[-1]
|
|
blurb, entries, footer = "\n", "", "\n==Archived Logs=="
|
|
if 'revisions' in parsed['query']['pages'][pageid]:
|
|
content = parsed['query']['pages'][pageid]['revisions'][0]['*']
|
|
blurb, entries = content.split('==', 1)
|
|
# ensure we keep only a single newline after the blurb
|
|
blurb = blurb.strip() + "\n"
|
|
entries = '==' + entries
|
|
try:
|
|
entries, footer = entries.rsplit('\n==Archived Logs==', 1)
|
|
footer = '\n==Archived Logs==' + footer
|
|
except ValueError:
|
|
pass
|
|
|
|
return blurb, entries, footer
|
|
|
|
def post_entry(self, wiki_page, content, summary, cookies):
|
|
"""
|
|
Post the new page contents 'content' to the page title 'wiki_page'
|
|
with a 'summary' using the login credentials from 'cookies'
|
|
|
|
@type wiki_page: string
|
|
@type content: string
|
|
@type summary: string
|
|
@type cookies: CookieJar
|
|
"""
|
|
|
|
params = ("?format=json&action=query&prop=info|revisions"
|
|
"&intoken=edit&rvprop=timestamp&titles=")
|
|
req = self.retry_request(self.wiki_uri+params+wiki_page,
|
|
cookies=cookies)
|
|
if not req:
|
|
return False
|
|
|
|
parsed = self.parse_json(req)
|
|
pageid = sorted(parsed['query']['pages'].keys())[-1]
|
|
edit_token = parsed['query']['pages'][pageid]['edittoken']
|
|
edit_cookie = cookies.copy()
|
|
edit_cookie.update(req.cookies)
|
|
|
|
content = content.encode('utf-8')
|
|
|
|
content_hash = hashlib.md5(content).hexdigest()
|
|
|
|
payload = {
|
|
'action': 'edit',
|
|
'assert': 'user',
|
|
'title': wiki_page,
|
|
'summary': summary,
|
|
'text': content,
|
|
'md5': content_hash,
|
|
'token': edit_token,
|
|
'utf8': '',
|
|
'format': 'json'
|
|
}
|
|
|
|
try:
|
|
req = requests.post(self.wiki_uri, data=payload,
|
|
cookies=edit_cookie, timeout=self.TIMEOUT)
|
|
except (requests.exceptions.RequestException,
|
|
requests.exceptions.Timeout):
|
|
return False
|
|
|
|
if not req.status_code == requests.codes.ok:
|
|
log.err("Unexpected status code %s received when trying to post"
|
|
" an entry to the wiki." % req.status_code)
|
|
return False
|
|
else:
|
|
result = self.parse_json(req)
|
|
status = result.get('edit', {}).get('result', '')
|
|
if status == 'Success':
|
|
return True
|
|
return False
|