2019-08-29 16:06:55 +02:00
|
|
|
import logging
|
|
|
|
|
2018-04-10 22:30:57 -05:00
|
|
|
from osc import conf
|
2019-03-05 08:40:47 -06:00
|
|
|
from osclib.conf import Config
|
2019-03-05 08:40:47 -06:00
|
|
|
from osclib.core import entity_email
|
2018-02-05 19:45:28 -06:00
|
|
|
from osclib.core import project_list_prefix
|
2019-02-15 10:55:39 -06:00
|
|
|
from osclib.memoize import memoize
|
2018-02-05 19:45:28 -06:00
|
|
|
|
2019-08-29 16:06:55 +02:00
|
|
|
logger = logging.getLogger()
|
2018-02-05 19:45:28 -06:00
|
|
|
|
2019-02-15 10:55:39 -06:00
|
|
|
@memoize(session=True)
|
2019-02-15 10:55:39 -06:00
|
|
|
def project_list_family(apiurl, project, include_update=False):
|
2018-02-05 19:45:28 -06:00
|
|
|
"""
|
|
|
|
Determine the available projects within the same product family.
|
|
|
|
|
|
|
|
Skips < SLE-12 due to format change.
|
|
|
|
"""
|
2019-02-15 10:55:39 -06:00
|
|
|
if project.endswith(':NonFree'):
|
|
|
|
project = project[:-8]
|
|
|
|
project_suffix = ':NonFree'
|
|
|
|
else:
|
|
|
|
project_suffix = ''
|
|
|
|
|
2018-02-05 19:45:28 -06:00
|
|
|
if project == 'openSUSE:Factory':
|
2019-02-15 10:55:39 -06:00
|
|
|
return [project + project_suffix]
|
2018-02-05 19:45:28 -06:00
|
|
|
|
2018-12-04 07:41:13 +01:00
|
|
|
if project.endswith(':ARM') or project.endswith(':PowerPC'):
|
2019-02-15 10:55:39 -06:00
|
|
|
return [project + project_suffix]
|
2018-12-04 07:41:13 +01:00
|
|
|
|
2018-02-05 19:45:28 -06:00
|
|
|
count_original = project.count(':')
|
|
|
|
if project.startswith('SUSE:SLE'):
|
|
|
|
project = ':'.join(project.split(':')[:2])
|
2019-02-15 10:55:39 -06:00
|
|
|
family_filter = lambda p: p.count(':') == count_original and (
|
|
|
|
p.endswith(':GA') or (include_update and p.endswith(':Update')))
|
2018-02-05 19:45:28 -06:00
|
|
|
else:
|
2019-02-15 10:55:39 -06:00
|
|
|
family_filter = lambda p: p.count(':') == count_original or (
|
|
|
|
include_update and p.count(':') == count_original + 1 and p.endswith(':Update'))
|
2018-02-05 19:45:28 -06:00
|
|
|
|
|
|
|
prefix = ':'.join(project.split(':')[:-1])
|
|
|
|
projects = project_list_prefix(apiurl, prefix)
|
2019-05-22 20:32:33 -05:00
|
|
|
projects = list(filter(family_filter, projects))
|
2019-02-15 10:55:39 -06:00
|
|
|
|
|
|
|
if project_suffix:
|
|
|
|
for i, project in enumerate(projects):
|
|
|
|
if project.endswith(':Update'):
|
|
|
|
projects[i] = project.replace(':Update', project_suffix + ':Update')
|
|
|
|
else:
|
|
|
|
projects[i] += project_suffix
|
2018-02-05 19:45:28 -06:00
|
|
|
|
2019-05-22 20:32:33 -05:00
|
|
|
return projects
|
2018-02-05 19:45:28 -06:00
|
|
|
|
2019-02-15 10:55:39 -06:00
|
|
|
def project_list_family_prior(apiurl, project, include_self=False, last=None, include_update=False):
|
2018-02-05 19:45:28 -06:00
|
|
|
"""
|
|
|
|
Determine the available projects within the same product family released
|
|
|
|
prior to the specified project.
|
|
|
|
"""
|
2019-02-15 10:55:39 -06:00
|
|
|
projects = project_list_family(apiurl, project, include_update)
|
2018-02-05 19:45:28 -06:00
|
|
|
past = False
|
|
|
|
prior = []
|
|
|
|
for entry in sorted(projects, key=project_list_family_sorter, reverse=True):
|
|
|
|
if entry == project:
|
|
|
|
past = True
|
|
|
|
if not include_self:
|
|
|
|
continue
|
|
|
|
|
|
|
|
if past:
|
|
|
|
prior.append(entry)
|
|
|
|
|
2018-02-12 17:55:39 -06:00
|
|
|
if entry == last:
|
|
|
|
break
|
|
|
|
|
2018-02-05 19:45:28 -06:00
|
|
|
return prior
|
|
|
|
|
2019-02-15 10:55:39 -06:00
|
|
|
def project_list_family_prior_pattern(apiurl, project_pattern, project=None, include_update=True):
|
|
|
|
project_prefix, project_suffix = project_pattern.split('*', 2)
|
|
|
|
if project:
|
|
|
|
project = project if project.startswith(project_prefix) else None
|
|
|
|
|
|
|
|
if project:
|
|
|
|
projects = project_list_family_prior(apiurl, project, include_update=include_update)
|
|
|
|
else:
|
|
|
|
if ':Leap:' in project_prefix:
|
|
|
|
project = project_prefix
|
|
|
|
|
|
|
|
if ':SLE-' in project_prefix:
|
|
|
|
project = project_prefix + ':GA'
|
|
|
|
|
|
|
|
projects = project_list_family(apiurl, project, include_update)
|
|
|
|
projects = sorted(projects, key=project_list_family_sorter, reverse=True)
|
|
|
|
|
|
|
|
return [p for p in projects if p.startswith(project_prefix)]
|
|
|
|
|
2018-02-05 19:45:28 -06:00
|
|
|
def project_list_family_sorter(project):
|
|
|
|
"""Extract key to be used as sorter (oldest to newest)."""
|
|
|
|
version = project_version(project)
|
|
|
|
|
|
|
|
if version >= 42:
|
|
|
|
version -= 42
|
|
|
|
|
|
|
|
if project.endswith(':Update'):
|
|
|
|
version += 0.01
|
|
|
|
|
|
|
|
return version
|
|
|
|
|
|
|
|
def project_version(project):
|
|
|
|
"""
|
|
|
|
Extract a float representation of the project version.
|
|
|
|
|
|
|
|
For example:
|
|
|
|
- openSUSE:Leap:15.0 -> 15.0
|
|
|
|
- openSUSE:Leap:42.3 -> 42.3
|
|
|
|
- SUSE:SLE-15:GA -> 15.0
|
|
|
|
- SUSE:SLE-15-SP1:GA -> 15.1
|
|
|
|
"""
|
|
|
|
if ':Leap:' in project:
|
|
|
|
return float(project.split(':')[2])
|
|
|
|
|
|
|
|
if ':SLE-' in project:
|
|
|
|
version = project.split(':')[1]
|
|
|
|
parts = version.split('-')
|
|
|
|
version = float(parts[1])
|
|
|
|
if len(parts) > 2:
|
|
|
|
# Add each service pack as a tenth.
|
|
|
|
version += float(parts[2][2:]) / 10
|
|
|
|
return version
|
|
|
|
|
2019-02-15 10:55:39 -06:00
|
|
|
return 0
|
2018-04-10 22:30:57 -05:00
|
|
|
|
2019-08-29 16:06:55 +02:00
|
|
|
def mail_send_with_details(relay, sender, subject, to, text, xmailer=None, followup_to=None, dry=True):
|
|
|
|
import smtplib
|
2018-04-10 22:30:57 -05:00
|
|
|
from email.mime.text import MIMEText
|
|
|
|
import email.utils
|
2019-08-29 16:06:55 +02:00
|
|
|
msg = MIMEText(text, _charset='UTF-8')
|
|
|
|
msg['Subject'] = subject
|
2018-04-10 22:30:57 -05:00
|
|
|
msg['Message-ID'] = email.utils.make_msgid()
|
|
|
|
msg['Date'] = email.utils.formatdate(localtime=1)
|
2019-08-29 16:06:55 +02:00
|
|
|
msg['From'] = sender
|
2018-04-10 22:30:57 -05:00
|
|
|
msg['To'] = to
|
|
|
|
if followup_to:
|
|
|
|
msg['Mail-Followup-To'] = followup_to
|
2019-08-29 16:06:55 +02:00
|
|
|
if xmailer:
|
|
|
|
msg.add_header('X-Mailer', xmailer)
|
|
|
|
msg.add_header('Precedence', 'bulk')
|
2018-04-10 22:30:57 -05:00
|
|
|
if dry:
|
2019-08-29 16:06:55 +02:00
|
|
|
logger.debug(msg.as_string())
|
2018-04-10 22:30:57 -05:00
|
|
|
return
|
2019-08-29 16:06:55 +02:00
|
|
|
logger.info("%s: %s", msg['To'], msg['Subject'])
|
|
|
|
s = smtplib.SMTP(relay)
|
|
|
|
s.sendmail(msg['From'], {msg['To'], sender }, msg.as_string())
|
2018-04-10 22:30:57 -05:00
|
|
|
s.quit()
|
2018-08-21 02:05:12 -05:00
|
|
|
|
2019-08-29 16:06:55 +02:00
|
|
|
def mail_send(apiurl, project, to, subject, body, from_key='maintainer',
|
|
|
|
followup_to_key='release-list', dry=False):
|
|
|
|
|
|
|
|
config = Config.get(apiurl, project)
|
|
|
|
if from_key is None:
|
|
|
|
sender = entity_email(apiurl, conf.get_apiurl_usr(apiurl), include_name=True)
|
|
|
|
else:
|
|
|
|
sender = config['mail-{}'.format(from_key)]
|
|
|
|
|
|
|
|
if '@' not in to:
|
|
|
|
to = config['mail-{}'.format(to)]
|
|
|
|
|
|
|
|
followup_to = config.get('mail-{}'.format(followup_to_key))
|
|
|
|
relay = config.get('mail-relay', 'relay.suse.de')
|
|
|
|
|
|
|
|
mail_send_with_details(text=body, subject=subject, relay=relay, sender=sender,
|
|
|
|
followup_to=followup_to, to=to, dry=dry)
|
|
|
|
|
2018-08-21 02:05:12 -05:00
|
|
|
def sha1_short(data):
|
|
|
|
import hashlib
|
|
|
|
|
|
|
|
if isinstance(data, list):
|
|
|
|
data = '::'.join(data)
|
|
|
|
|
2019-05-16 08:02:20 +02:00
|
|
|
if isinstance(data, str):
|
|
|
|
data = data.encode('utf-8')
|
|
|
|
|
|
|
|
return hashlib.sha1(data).hexdigest()[:7]
|
2019-09-18 21:10:49 -05:00
|
|
|
|
|
|
|
def rmtree_nfs_safe(path, attempts=5):
|
|
|
|
import shutil
|
|
|
|
try:
|
|
|
|
shutil.rmtree(path)
|
|
|
|
except OSError as e:
|
|
|
|
# Directory not empty due to slow filesystem (see #1326 old occurance).
|
|
|
|
if attempts <= 0 or e.errno != 39:
|
|
|
|
raise e
|
|
|
|
|
|
|
|
from time import sleep
|
|
|
|
sleep(0.25)
|
|
|
|
|
|
|
|
rmtree_nfs_safe(path, attempts - 1)
|