Hello community, here is the log from the commit of package salt for openSUSE:Factory checked in at 2016-01-26 10:14:44 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/salt (Old) and /work/SRC/openSUSE:Factory/.salt.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "salt" Changes: -------- --- /work/SRC/openSUSE:Factory/salt/salt.changes 2015-12-23 08:49:48.000000000 +0100 +++ /work/SRC/openSUSE:Factory/.salt.new/salt.changes 2016-01-26 10:14:45.000000000 +0100 @@ -1,0 +2,26 @@ +Fri Jan 15 13:16:46 UTC 2016 - [email protected] + +- Fix zypper module info_available on SLE-11 + * add salt-2015.8-zypper-info.patch + * https://github.com/saltstack/salt/pull/30384 +- zypper/pkg: add package attributes filtering + * add salt-2015.8-pkg-zypper-attr-filtering.patch + * https://github.com/saltstack/salt/pull/30267 +- Remove obsoleted patches and fixes: + * 0001-Add-rpm.minimal_info-fix-rpm.info.patch + * 0002-Reduce-information-returned-from-pkg.info_installed.patch + * Remove require on glibc-locale (bsc#959572) + +------------------------------------------------------------------- +Wed Jan 13 12:03:06 UTC 2016 - [email protected] + +- Add missing return data to scheduled jobs + * add salt-2015.8-schedule-ret.patch for + * https://github.com/saltstack/salt/pull/30246 + +------------------------------------------------------------------- +Mon Dec 21 14:06:27 UTC 2015 - [email protected] + +- Update zypper-utf-8.patch for Python 2.6 + +------------------------------------------------------------------- Old: ---- 0001-Add-rpm.minimal_info-fix-rpm.info.patch 0002-Reduce-information-returned-from-pkg.info_installed.patch New: ---- salt-2015.8-pkg-zypper-attr-filtering.patch salt-2015.8-schedule-ret.patch salt-2015.8-zypper-info.patch ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ salt.spec ++++++ --- /var/tmp/diff_new_pack.Bpkufl/_old 2016-01-26 10:14:46.000000000 +0100 +++ /var/tmp/diff_new_pack.Bpkufl/_new 2016-01-26 10:14:46.000000000 +0100 @@ -1,7 +1,7 @@ # # spec file for package salt # -# Copyright (c) 2015 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2016 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -53,11 +53,12 @@ Patch3: 1efe484309a5c776974e723f3da0f5181f4bdb86.patch # PATCH-FIX-OPENSUSE detect bad UTF-8 in package header, bsc#958350 Patch4: zypper-utf-8.patch -# PATCH-FIX-OPENSUSE report epoch and architecture -Patch5: 0001-Add-rpm.minimal_info-fix-rpm.info.patch -# PATCH-FIX-OPENSUSE use minimal_info for pkg.info_installed -Patch6: 0002-Reduce-information-returned-from-pkg.info_installed.patch - +# PATCH-FIX-UPSTREAM https://github.com/saltstack/salt/pull/30246 +Patch5: salt-2015.8-schedule-ret.patch +# PATCH-FIX-UPSTREAM https://github.com/saltstack/salt/pull/30267 +Patch6: salt-2015.8-pkg-zypper-attr-filtering.patch +# PATCH-FIX-UPSTREAM https://github.com/saltstack/salt/pull/30384 +Patch7: salt-2015.8-zypper-info.patch BuildRoot: %{_tmppath}/%{name}-%{version}-build BuildRequires: logrotate BuildRequires: python @@ -105,8 +106,6 @@ %if 0%{?suse_version} Requires(pre): %fillup_prereq Requires(pre): pwdutils -# bsc#959572 -Requires: glibc-locale %endif Requires: logrotate Requires: python @@ -405,6 +404,7 @@ %patch4 -p1 %patch5 -p1 %patch6 -p1 +%patch7 -p1 %build python setup.py --salt-transport=both build ++++++ salt-2015.8-pkg-zypper-attr-filtering.patch ++++++ diff --git a/salt/modules/rpm.py b/salt/modules/rpm.py index 7810e22..51c72c9 100644 --- a/salt/modules/rpm.py +++ b/salt/modules/rpm.py @@ -8,7 +8,6 @@ from __future__ import absolute_import import logging import os import re -import time import datetime # Import Salt libs @@ -399,24 +398,20 @@ def diff(package, path): return res -def _pkg_time_to_iso(pkg_time): - ''' - Convert package time to ISO 8601. - - :param pkg_time: - :return: - ''' - ptime = time.strptime(pkg_time, '%a %d %b %Y %H:%M:%S %p %Z') - return datetime.datetime(ptime.tm_year, ptime.tm_mon, ptime.tm_mday, - ptime.tm_hour, ptime.tm_min, ptime.tm_sec).isoformat() + "Z" - - -def info(*packages): +def info(*packages, **attr): ''' Return a detailed package(s) summary information. If no packages specified, all packages will be returned. :param packages: + + :param attr: + Comma-separated package attributes. If no 'attr' is specified, all available attributes returned. + + Valid attributes are: + version, vendor, release, build_date, build_date_time_t, install_date, install_date_time_t, + build_host, group, source_rpm, arch, epoch, size, license, signature, packager, url, summary, description. + :return: CLI example: @@ -424,30 +419,59 @@ def info(*packages): .. code-block:: bash salt '*' lowpkg.info apache2 bash + salt '*' lowpkg.info apache2 bash attr=version + salt '*' lowpkg.info apache2 bash attr=version,build_date_iso,size ''' cmd = packages and "rpm -q {0}".format(' '.join(packages)) or "rpm -qa" - # Locale needs to be en_US instead of C, because RPM otherwise will yank the timezone from the timestamps - call = __salt__['cmd.run_all'](cmd + (" --queryformat 'Name: %{NAME}\n" - "Relocations: %|PREFIXES?{[%{PREFIXES} ]}:{(not relocatable)}|\n" - "Version: %{VERSION}\n" - "Vendor: %{VENDOR}\n" - "Release: %{RELEASE}\n" - "Build Date: %{BUILDTIME:date}\n" - "Install Date: %|INSTALLTIME?{%{INSTALLTIME:date}}:{(not installed)}|\n" - "Build Host: %{BUILDHOST}\n" - "Group: %{GROUP}\n" - "Source RPM: %{SOURCERPM}\n" - "Size: %{LONGSIZE}\n" - "%|LICENSE?{License: %{LICENSE}\n}|" - "Signature: %|DSAHEADER?{%{DSAHEADER:pgpsig}}:{%|RSAHEADER?{%{RSAHEADER:pgpsig}}:{%|SIGGPG?{%{SIGGPG:pgpsig}}:{%|SIGPGP?{%{SIGPGP:pgpsig}}:{(none)}|}|}|}|\n" - "%|PACKAGER?{Packager: %{PACKAGER}\n}|" - "%|URL?{URL: %{URL}\n}|" - "Summary: %{SUMMARY}\n" - "Description:\n%{DESCRIPTION}\n" - "-----\n'"), - output_loglevel='trace', env={'LC_ALL': 'en_US', 'TZ': 'UTC'}, clean_env=True) + # Construct query format + attr_map = { + "name": "name: %{NAME}\\n", + "relocations": "relocations: %|PREFIXES?{[%{PREFIXES} ]}:{(not relocatable)}|\\n", + "version": "version: %{VERSION}\\n", + "vendor": "vendor: %{VENDOR}\\n", + "release": "release: %{RELEASE}\\n", + "epoch": "%|EPOCH?{epoch: %{EPOCH}\\n}|", + "build_date_time_t": "build_date_time_t: %{BUILDTIME}\\n", + "build_date": "build_date: %{BUILDTIME}\\n", + "install_date_time_t": "install_date_time_t: %|INSTALLTIME?{%{INSTALLTIME}}:{(not installed)}|\\n", + "install_date": "install_date: %|INSTALLTIME?{%{INSTALLTIME}}:{(not installed)}|\\n", + "build_host": "build_host: %{BUILDHOST}\\n", + "group": "group: %{GROUP}\\n", + "source_rpm": "source_rpm: %{SOURCERPM}\\n", + "size": "size: %{LONGSIZE}\\n", + "arch": "arch: %{ARCH}\\n", + "license": "%|LICENSE?{license: %{LICENSE}\\n}|", + "signature": "signature: %|DSAHEADER?{%{DSAHEADER:pgpsig}}:{%|RSAHEADER?{%{RSAHEADER:pgpsig}}:" + "{%|SIGGPG?{%{SIGGPG:pgpsig}}:{%|SIGPGP?{%{SIGPGP:pgpsig}}:{(none)}|}|}|}|\\n", + "packager": "%|PACKAGER?{packager: %{PACKAGER}\\n}|", + "url": "%|URL?{url: %{URL}\\n}|", + "summary": "summary: %{SUMMARY}\\n", + "description": "description:\\n%{DESCRIPTION}\\n", + } + + attr = attr.get('attr', None) and attr['attr'].split(",") or None + query = list() + if attr: + for attr_k in attr: + if attr_k in attr_map and attr_k != 'description': + query.append(attr_map[attr_k]) + if not query: + raise CommandExecutionError('No valid attributes found.') + if 'name' not in attr: + attr.append('name') + query.append(attr_map['name']) + else: + for attr_k, attr_v in attr_map.iteritems(): + if attr_k != 'description': + query.append(attr_v) + if attr and 'description' in attr or not attr: + query.append(attr_map['description']) + query.append("-----\\n") + + call = __salt__['cmd.run_all'](cmd + (" --queryformat '{0}'".format(''.join(query))), + output_loglevel='trace', env={'TZ': 'UTC'}, clean_env=True) if call['retcode'] != 0: comment = '' if 'stderr' in call: @@ -477,17 +501,31 @@ def info(*packages): if len(line) != 2: continue key, value = line - key = key.replace(' ', '_').lower() if key == 'description': descr_marker = True continue if key == 'name': pkg_name = value + + # Convert Unix ticks into ISO time format if key in ['build_date', 'install_date']: - value = _pkg_time_to_iso(value) - if key != 'description' and value: + try: + pkg_data[key] = datetime.datetime.fromtimestamp(int(value)).isoformat() + "Z" + except ValueError: + log.warning('Could not convert "{0}" into Unix time'.format(value)) + continue + + # Convert Unix ticks into an Integer + if key in ['build_date_time_t', 'install_date_time_t']: + try: + pkg_data[key] = int(value) + except ValueError: + log.warning('Could not convert "{0}" into Unix time'.format(value)) + continue + if key not in ['description', 'name'] and value: pkg_data[key] = value - pkg_data['description'] = os.linesep.join(descr) + if attr and 'description' in attr or not attr: + pkg_data['description'] = os.linesep.join(descr) if pkg_name: ret[pkg_name] = pkg_data diff --git a/salt/modules/zypper.py b/salt/modules/zypper.py index 84b5d51..ccba713 100644 --- a/salt/modules/zypper.py +++ b/salt/modules/zypper.py @@ -96,19 +96,32 @@ def list_upgrades(refresh=True): list_updates = salt.utils.alias_function(list_upgrades, 'list_updates') -def info_installed(*names): +def info_installed(*names, **attr): ''' Return the information of the named package(s), installed on the system. + :param names: + Names of the packages to get information about. + + :param attr: + Comma-separated package attributes. If no 'attr' is specified, all available attributes returned. + + Valid attributes are: + version, vendor, release, build_date, build_date_time_t, install_date, install_date_time_t, + build_host, group, source_rpm, arch, epoch, size, license, signature, packager, url, + summary, description. + CLI example: .. code-block:: bash salt '*' pkg.info_installed <package1> salt '*' pkg.info_installed <package1> <package2> <package3> ... + salt '*' pkg.info_installed <package1> attr=version,vendor + salt '*' pkg.info_installed <package1> <package2> <package3> ... attr=version,vendor ''' ret = dict() - for pkg_name, pkg_nfo in __salt__['lowpkg.info'](*names).items(): + for pkg_name, pkg_nfo in __salt__['lowpkg.info'](*names, **attr).items(): t_nfo = dict() # Translate dpkg-specific keys to a common structure for key, value in pkg_nfo.items(): ++++++ salt-2015.8-schedule-ret.patch ++++++ diff --git a/salt/utils/schedule.py b/salt/utils/schedule.py index 4458202..cae5fcf 100644 --- a/salt/utils/schedule.py +++ b/salt/utils/schedule.py @@ -482,24 +482,24 @@ class Schedule(object): func = None if func not in self.functions: log.info( - 'Invalid function: {0} in job {1}. Ignoring.'.format( + 'Invalid function: {0} in scheduled job {1}.'.format( func, name ) ) + + if 'name' not in data: + data['name'] = name + log.info( + 'Running Job: {0}.'.format(name) + ) + if self.opts.get('multiprocessing', True): + thread_cls = multiprocessing.Process else: - if 'name' not in data: - data['name'] = name - log.info( - 'Running Job: {0}.'.format(name) - ) - if self.opts.get('multiprocessing', True): - thread_cls = multiprocessing.Process - else: - thread_cls = threading.Thread - proc = thread_cls(target=self.handle_func, args=(func, data)) - proc.start() - if self.opts.get('multiprocessing', True): - proc.join() + thread_cls = threading.Thread + proc = thread_cls(target=self.handle_func, args=(func, data)) + proc.start() + if self.opts.get('multiprocessing', True): + proc.join() def enable_schedule(self): ''' @@ -642,33 +642,39 @@ class Schedule(object): except OSError: log.info('Unable to remove file: {0}.'.format(fn_)) - salt.utils.daemonize_if(self.opts) + try: + salt.utils.daemonize_if(self.opts) - ret['pid'] = os.getpid() + ret['pid'] = os.getpid() - if 'jid_include' not in data or data['jid_include']: - log.debug('schedule.handle_func: adding this job to the jobcache ' - 'with data {0}'.format(ret)) - # write this to /var/cache/salt/minion/proc - with salt.utils.fopen(proc_fn, 'w+b') as fp_: - fp_.write(salt.payload.Serial(self.opts).dumps(ret)) - - args = tuple() - if 'args' in data: - args = data['args'] - - kwargs = {} - if 'kwargs' in data: - kwargs = data['kwargs'] - # if the func support **kwargs, lets pack in the pub data we have - # TODO: pack the *same* pub data as a minion? - argspec = salt.utils.args.get_function_argspec(self.functions[func]) - if argspec.keywords: - # this function accepts **kwargs, pack in the publish data - for key, val in six.iteritems(ret): - kwargs['__pub_{0}'.format(key)] = val + if 'jid_include' not in data or data['jid_include']: + log.debug('schedule.handle_func: adding this job to the jobcache ' + 'with data {0}'.format(ret)) + # write this to /var/cache/salt/minion/proc + with salt.utils.fopen(proc_fn, 'w+b') as fp_: + fp_.write(salt.payload.Serial(self.opts).dumps(ret)) + + args = tuple() + if 'args' in data: + args = data['args'] + + kwargs = {} + if 'kwargs' in data: + kwargs = data['kwargs'] + + if func not in self.functions: + ret['return'] = self.functions.missing_fun_string(func) + salt.utils.error.raise_error( + message=self.functions.missing_fun_string(func)) + + # if the func support **kwargs, lets pack in the pub data we have + # TODO: pack the *same* pub data as a minion? + argspec = salt.utils.args.get_function_argspec(self.functions[func]) + if argspec.keywords: + # this function accepts **kwargs, pack in the publish data + for key, val in six.iteritems(ret): + kwargs['__pub_{0}'.format(key)] = val - try: ret['return'] = self.functions[func](*args, **kwargs) data_returner = data.get('returner', None) @@ -694,28 +700,34 @@ class Schedule(object): ) ) - # Only attempt to return data to the master - # if the scheduled job is running on a minion. - if '__role' in self.opts and self.opts['__role'] == 'minion': - if 'return_job' in data and not data['return_job']: - pass - else: - # Send back to master so the job is included in the job list - mret = ret.copy() - mret['jid'] = 'req' - channel = salt.transport.Channel.factory(self.opts, usage='salt_schedule') - load = {'cmd': '_return', 'id': self.opts['id']} - for key, value in six.iteritems(mret): - load[key] = value - channel.send(load) - + ret['retcode'] = self.functions.pack['__context__']['retcode'] + ret['success'] = True except Exception: log.exception("Unhandled exception running {0}".format(ret['fun'])) # Although catch-all exception handlers are bad, the exception here # is to let the exception bubble up to the top of the thread context, # where the thread will die silently, which is worse. + if 'return' not in ret: + ret['return'] = "Unhandled exception running {0}".format(ret['fun']) + ret['success'] = False + ret['retcode'] = 254 finally: try: + # Only attempt to return data to the master + # if the scheduled job is running on a minion. + if '__role' in self.opts and self.opts['__role'] == 'minion': + if 'return_job' in data and not data['return_job']: + pass + else: + # Send back to master so the job is included in the job list + mret = ret.copy() + mret['jid'] = 'req' + channel = salt.transport.Channel.factory(self.opts, usage='salt_schedule') + load = {'cmd': '_return', 'id': self.opts['id']} + for key, value in six.iteritems(mret): + load[key] = value + channel.send(load) + log.debug('schedule.handle_func: Removing {0}'.format(proc_fn)) os.unlink(proc_fn) except OSError as exc: @@ -757,11 +769,10 @@ class Schedule(object): func = None if func not in self.functions: log.info( - 'Invalid function: {0} in job {1}. Ignoring.'.format( + 'Invalid function: {0} in scheduled job {1}.'.format( func, job ) ) - continue if 'name' not in data: data['name'] = job # Add up how many seconds between now and then ++++++ salt-2015.8-zypper-info.patch ++++++ diff --git a/salt/modules/zypper.py b/salt/modules/zypper.py index ccba713..9d654a2 100644 --- a/salt/modules/zypper.py +++ b/salt/modules/zypper.py @@ -164,12 +164,14 @@ def info_available(*names, **kwargs): # Run in batches while batch: cmd = 'zypper info -t package {0}'.format(' '.join(batch[:batch_size])) - pkg_info.extend(re.split(r"----*", __salt__['cmd.run_stdout'](cmd, output_loglevel='trace'))) + pkg_info.extend(re.split(r"Information for package*", __salt__['cmd.run_stdout'](cmd, output_loglevel='trace'))) batch = batch[batch_size:] for pkg_data in pkg_info: nfo = {} for line in [data for data in pkg_data.split("\n") if ":" in data]: + if line.startswith("-----"): + continue kw = [data.strip() for data in line.split(":", 1)] if len(kw) == 2 and kw[1]: nfo[kw[0].lower()] = kw[1] ++++++ zypper-utf-8.patch ++++++ --- /var/tmp/diff_new_pack.Bpkufl/_old 2016-01-26 10:14:46.000000000 +0100 +++ /var/tmp/diff_new_pack.Bpkufl/_new 2016-01-26 10:14:46.000000000 +0100 @@ -1,15 +1,40 @@ -diff -wruN -x '*~' -x '*.o' -x '*.a' -x '*.so' -x '*.so.[0-9]' -x autom4te.cache -x .deps -x .libs ../orig-salt-2015.8.3/salt/modules/zypper.py ./salt/modules/zypper.py ---- ../orig-salt-2015.8.3/salt/modules/zypper.py 2015-12-01 22:25:13.000000000 +0100 -+++ ./salt/modules/zypper.py 2015-12-09 09:15:41.157266587 +0100 -@@ -112,6 +112,11 @@ +From 8fcc530f0473e9fcd5a7099617516a6d184b5303 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Klaus=20K=C3=A4mpf?= <[email protected]> +Date: Thu, 10 Dec 2015 13:21:41 +0100 +Subject: [PATCH] zypper: check package header content for valid utf-8 + +"salt 'system.domain.tld' pkg.info_installed --out json" can crash with + + [ERROR ] An un-handled exception was caught by salt's global exception handler: + UnicodeDecodeError: 'utf8' codec can't decode byte ... + +if a package name, summary, or description contains invalid UTF-8. + +This patch detects such rpm header values, logs them as errors, and +replaces them with "*** Bad UTF-8 ***". + +Update: drop the 'errors=' named parameter from the encode() call as it +is incompatible with Python 2.6 as used in SLE 11. +--- + salt/modules/zypper.py | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/salt/modules/zypper.py b/salt/modules/zypper.py +index be28311ae1bd..483d63e08edc 100644 +--- a/salt/modules/zypper.py ++++ b/salt/modules/zypper.py +@@ -115,6 +115,11 @@ def info_installed(*names): t_nfo = dict() # Translate dpkg-specific keys to a common structure for key, value in pkg_nfo.items(): + try: -+ value = value.encode('UTF-8', errors='replace') ++ value = value.encode('UTF-8', 'replace') + except(UnicodeDecodeError): -+ log.error('Package ' + pkg_name + ' has bad UTF-8 code in ' + key + ': ' + value) ++ log.error('Package {0} has bad UTF-8 code in {1}: {2}'.format(pkg_name, key, value)) + value = '*** Bad UTF-8 ***' if key == 'source_rpm': t_nfo['source'] = value else: +-- +2.6.3 +
