blob: 786b1ca8a713432ae3f80c13a22f8ddbb7c5196c [file] [log] [blame]
from copy import copy
import os
import re
from util.algorithms import getChunk
class UpdateVerifyError(Exception):
pass
class UpdateVerifyConfig(object):
comment_regex = re.compile("^#")
key_write_order = ("release", "product", "platform", "build_id", "locales",
"channel", "patch_types", "from", "aus_server",
"ftp_server_from", "ftp_server_to", "to",
"mar_channel_IDs")
global_keys = ("product", "channel", "aus_server", "to")
release_keys = ("release", "build_id", "locales", "patch_types", "from",
"ftp_server_from", "ftp_server_to", "mar_channel_IDs",
"platform")
first_only_keys = ("from", "aus_server", "to")
compare_attrs = global_keys + ("releases",)
def __init__(self, product=None, channel=None,
aus_server=None, to=None):
self.product = product
self.channel = channel
self.aus_server = aus_server
self.to = to
self.releases = []
def __eq__(self, other):
self_list = [getattr(self, attr) for attr in self.compare_attrs]
other_list = [getattr(other, attr) for attr in self.compare_attrs]
return self_list == other_list
def __ne__(self, other):
return not self.__eq__(other)
def _parseLine(self, line):
entry = {}
items = re.findall("\w+=[\"'][^\"']*[\"']", line)
for i in items:
m = re.search(
"(?P<key>\w+)=[\"'](?P<value>.+)[\"']", i).groupdict()
if m["key"] not in self.global_keys and m["key"] not in self.release_keys:
raise UpdateVerifyError(
"Unknown key '%s' found on line:\n%s" % (m["key"], line))
if m["key"] in entry:
raise UpdateVerifyError("Multiple values found for key '%s' on line:\n%s" % (m["key"], line))
entry[m["key"]] = m["value"]
if not entry:
raise UpdateVerifyError("No parseable data in line '%s'" % line)
return entry
def _addEntry(self, entry, first):
releaseKeys = {}
for k, v in entry.items():
if k in self.global_keys:
setattr(self, k, entry[k])
elif k in self.release_keys:
# "from" is reserved in Python
if k == "from":
releaseKeys["from_path"] = v
else:
releaseKeys[k] = v
self.addRelease(**releaseKeys)
def read(self, config):
f = open(config)
# Only the first non-comment line of an update verify config should
# have a "from" and"ausServer". Ignore any subsequent lines with them.
first = True
for line in f.readlines():
# Skip comment lines
if self.comment_regex.search(line):
continue
self._addEntry(self._parseLine(line), first)
first = False
def write(self, fh):
first = True
for releaseInfo in self.releases:
for key in self.key_write_order:
if key in self.global_keys and (first or key not in self.first_only_keys):
value = getattr(self, key)
elif key in self.release_keys:
value = releaseInfo[key]
else:
value = None
if value is not None:
fh.write(key)
fh.write("=")
if isinstance(value, (list, tuple)):
fh.write('"%s" ' % " ".join(value))
else:
fh.write('"%s" ' % str(value))
# Rewind one character to avoid having a trailing space
fh.seek(-1, os.SEEK_CUR)
fh.write("\n")
first = False
def addRelease(self, release=None, build_id=None, locales=[],
patch_types=['complete'], from_path=None,
ftp_server_from=None, ftp_server_to=None,
mar_channel_IDs=None, platform=None):
"""Locales and patch_types can be passed as either a string or a list.
If a string is passed, they will be converted to a list for internal
storage"""
if self.getRelease(build_id, from_path):
raise UpdateVerifyError("Couldn't add release identified by build_id '%s' and from_path '%s': already exists in config" % (build_id, from_path))
if isinstance(locales, basestring):
locales = sorted(list(locales.split()))
if isinstance(patch_types, basestring):
patch_types = list(patch_types.split())
self.releases.append({
"release": release,
"build_id": build_id,
"locales": locales,
"patch_types": patch_types,
"from": from_path,
"ftp_server_from": ftp_server_from,
"ftp_server_to": ftp_server_to,
"mar_channel_IDs": mar_channel_IDs,
"platform": platform,
})
def addLocaleToRelease(self, build_id, locale, from_path=None):
r = self.getRelease(build_id, from_path)
if not r:
raise UpdateVerifyError("Couldn't add '%s' to release identified by build_id '%s' and from_path '%s': '%s' doesn't exist in this config." % (locale, build_id, from_path, build_id))
r["locales"].append(locale)
r["locales"] = sorted(r["locales"])
def getRelease(self, build_id, from_path):
for r in self.releases:
if r["build_id"] == build_id and r["from"] == from_path:
return r
return {}
def getFullReleaseTests(self):
return [r for r in self.releases if r["from"] is not None]
def getQuickReleaseTests(self):
return [r for r in self.releases if r["from"] is None]
def getChunk(self, chunks, thisChunk):
fullTests = []
quickTests = []
for test in self.getFullReleaseTests():
for locale in test["locales"]:
fullTests.append([test["build_id"], locale, test["from"]])
for test in self.getQuickReleaseTests():
for locale in test["locales"]:
quickTests.append([test["build_id"], locale, test["from"]])
allTests = getChunk(fullTests, chunks, thisChunk)
allTests.extend(getChunk(quickTests, chunks, thisChunk))
newConfig = UpdateVerifyConfig(self.product, self.channel,
self.aus_server, self.to)
for t in allTests:
build_id, locale, from_path = t
if from_path == "None":
from_path = None
r = self.getRelease(build_id, from_path)
try:
newConfig.addRelease(r["release"], build_id, locales=[],
ftp_server_from=r["ftp_server_from"],
ftp_server_to=r["ftp_server_to"],
patch_types=r["patch_types"], from_path=from_path,
mar_channel_IDs=r["mar_channel_IDs"],
platform=r["platform"])
except UpdateVerifyError:
pass
newConfig.addLocaleToRelease(build_id, locale, from_path)
return newConfig