aboutsummaryrefslogtreecommitdiff
path: root/utils/mangle-jobs/mangle-jobs
diff options
context:
space:
mode:
Diffstat (limited to 'utils/mangle-jobs/mangle-jobs')
-rwxr-xr-xutils/mangle-jobs/mangle-jobs224
1 files changed, 0 insertions, 224 deletions
diff --git a/utils/mangle-jobs/mangle-jobs b/utils/mangle-jobs/mangle-jobs
deleted file mode 100755
index 9c465e9..0000000
--- a/utils/mangle-jobs/mangle-jobs
+++ /dev/null
@@ -1,224 +0,0 @@
-#!/usr/bin/python
-"""Helper to mass-edit jobs in jenkins.
-
-"""
-
-###############################################################################
-# Copyright (c) 2011 Linaro
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Eclipse Public License v1.0
-# which accompanies this distribution, and is available at
-# http://www.eclipse.org/legal/epl-v10.html
-###############################################################################
-
-import base64
-from contextlib import nested
-import json
-import os
-import sys
-import copy
-import re
-from tempfile import NamedTemporaryFile
-import urllib2
-import optparse
-import getpass
-from xml.dom import minidom
-
-from lxml.etree import fromstring, tostring
-
-
-optparser = optparse.OptionParser(usage="%prog <mangle script>")
-optparser.add_option("--url", default="http://localhost:8080/jenkins/",
- help="Jenkins base url, default: %default")
-optparser.add_option("--user",
- help="Jenkins username")
-optparser.add_option("--passwd-file", metavar="FILE",
- help="File holding Jenkins password")
-optparser.add_option("--really", action="store_true",
- help="Actually perform changes")
-optparser.add_option("--filter-jobname",
- help="Process only jobs matching regex pattern")
-optparser.add_option("--limit", type="int", default=-1,
- help="Change at most LIMIT jobs")
-optparser.add_option("--file",
- help="Process a file instead of all jobs on a remote server")
-
-options, args = optparser.parse_args(sys.argv[1:])
-if len(args) != 1:
- optparser.error("Wrong number of arguments")
-
-d = {}
-execfile(args[0], d, d)
-mangler = d['mangle']
-
-password = None
-if options.passwd_file:
- password = open(options.passwd_file).read().strip()
-elif not options.file:
- password = getpass.getpass("Password/API Token:")
-
-if options.url[-1] != '/':
- options.url += '/'
-
-auth_headers = {
- 'Authorization': 'Basic %s' % (
- base64.encodestring('%s:%s' % (options.user, password))[:-1],),
- }
-
-def _authJenkins(jenkins_path, data=None, extra_headers=None):
- """Make an authenticated request to jenkins.
-
- @param jenkins_path: The path on the Jenkins instance to make the request
- to.
- @param data: Data to include in the request (if this is not None the
- request will be a POST).
- @param extra_headers: A dictionary of extra headers that will passed in
- addition to Authorization.
- @raises urllib2.HTTPError: If the response is not a HTTP 200.
- @returns: the body of the response.
- """
- headers = auth_headers.copy()
- if extra_headers:
- headers.update(extra_headers)
- req = urllib2.Request(
- options.url + jenkins_path, data, headers)
- resp = urllib2.urlopen(req)
- return resp.read()
-
-def getJobConfig(job_name):
- return _authJenkins('job/' + job_name + '/config.xml')
-
-def postConfig(url, configXml, extra_headers=None):
- headers = {'Content-Type': 'text/xml', }
- if extra_headers is not None:
- headers.update(extra_headers)
- _authJenkins(url, configXml, headers)
-
-def render_xml(tree):
- # Render XML to exact dialect used by Jenkins
- # This involves some dirty magic
- text = tostring(tree, xml_declaration=True, encoding='UTF-8')
- # Roundtrip via minidom, this takes care of encoding " as entities
- tree2 = minidom.parseString(text)
- text = tree2.toxml('UTF-8')
-
- # expand empty tags
- text = re.sub(r"<([-A-Za-z.]+)/>", "<\\1></\\1>", text)
-
- # Some CR noise should be entities
- text = text.replace("\r", "&#xd;")
-
- # Finally, munge xml decl
- line1, rest = text.split("><", 1)
- line1 = line1.replace('"', "'")
- r = line1 + ">\n<" + rest
-
- return r
-
-def show_diff(old, new):
- with nested(NamedTemporaryFile(), NamedTemporaryFile()) as (a, b):
- a.write(old)
- b.write(new)
- a.flush(); b.flush()
- os.system('diff -u %s %s' % (a.name, b.name))
- print
-
-def indent_tree(elem, level=0):
- "Indent XML tree for pretty-printing"
- i = "\n" + level*" "
- if len(elem):
- if not elem.text or not elem.text.strip():
- elem.text = i + " "
- if not elem.tail or not elem.tail.strip():
- elem.tail = i
- for elem in elem:
- indent_tree(elem, level+1)
- if not elem.tail or not elem.tail.strip():
- elem.tail = i
- else:
- if level and (not elem.tail or not elem.tail.strip()):
- elem.tail = i
-
-def normalize2text(tree):
- """Return normalized text representation of XML tree, suitable for
- diffing with normal diff tool."""
- normalized = copy.deepcopy(tree)
- indent_tree(normalized)
- return tostring(normalized)
-
-def match_job_name(job_name):
- "Check if job name matches filters which may be specified on command line."
- if not options.filter_jobname:
- return True
- neg = False
- r = options.filter_jobname
- if r[0] == "-":
- neg = True
- r = r[1:]
- return bool(re.search(r, job_name)) ^ neg
-
-def get_csrf_token():
- try:
- crumb_data = _authJenkins('crumbIssuer/api/xml')
- except urllib2.HTTPError:
- # Ignore errors for android-build which provides no crumb.
- return None
- tree = minidom.parseString(crumb_data)
- crumb_tag = tree.getElementsByTagName('crumb')[0]
- field_tag = tree.getElementsByTagName('crumbRequestField')[0]
- crumb = str(crumb_tag.firstChild.wholeText)
- field = str(field_tag.firstChild.wholeText)
- return (field, crumb)
-
-def process_remote_jenkins():
- jobs = json.load(urllib2.urlopen(options.url + 'api/json?tree=jobs[name]'))
- names = [job['name'] for job in jobs['jobs']]
- names = [name for name in names if name == 'blank' or '_' in name]
- limit = options.limit
-
- csrf_token = get_csrf_token()
- if csrf_token is None:
- extra_headers = None
- else:
- extra_headers = { csrf_token[0]: csrf_token[1], }
-
- for name in names:
- if not match_job_name(name):
- continue
- if limit == 0:
- break
- limit -= 1
- print "Processing:" + name
- sys.stdout.flush()
- org_text = getJobConfig(name)
- tree = fromstring(org_text)
- org_normalized = normalize2text(tree)
-
- if mangler(tree) == False:
- continue
-
- if not options.really:
- new_normalized = normalize2text(tree)
- show_diff(org_normalized, new_normalized)
- else:
- new_text = render_xml(tree)
- if type(new_text) == type(u""):
- new_text = new_text.encode("utf8")
- postConfig(str('job/' + name + '/config.xml'), new_text,
- extra_headers)
-
-
-def main():
- if options.file:
- text = open(options.file).read()
- tree = fromstring(text)
-
- org_normalized = normalize2text(tree)
- mangler(tree)
- new_normalized = normalize2text(tree)
- show_diff(org_normalized, new_normalized)
- else:
- process_remote_jenkins()
-
-if __name__ == "__main__":
- main()