|
Packit |
6f3914 |
# emitter.py
|
|
Packit |
6f3914 |
# Emitters for dnf-automatic.
|
|
Packit |
6f3914 |
#
|
|
Packit |
6f3914 |
# Copyright (C) 2014-2016 Red Hat, Inc.
|
|
Packit |
6f3914 |
#
|
|
Packit |
6f3914 |
# This copyrighted material is made available to anyone wishing to use,
|
|
Packit |
6f3914 |
# modify, copy, or redistribute it subject to the terms and conditions of
|
|
Packit |
6f3914 |
# the GNU General Public License v.2, or (at your option) any later version.
|
|
Packit |
6f3914 |
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
Packit |
6f3914 |
# ANY WARRANTY expressed or implied, including the implied warranties of
|
|
Packit |
6f3914 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
|
Packit |
6f3914 |
# Public License for more details. You should have received a copy of the
|
|
Packit |
6f3914 |
# GNU General Public License along with this program; if not, write to the
|
|
Packit |
6f3914 |
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
Packit |
6f3914 |
# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
|
|
Packit |
6f3914 |
# source code or documentation are not subject to the GNU General Public
|
|
Packit |
6f3914 |
# License and may only be used or replicated with the express permission of
|
|
Packit |
6f3914 |
# Red Hat, Inc.
|
|
Packit |
6f3914 |
#
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
from __future__ import absolute_import
|
|
Packit |
6f3914 |
from __future__ import print_function
|
|
Packit |
6f3914 |
from __future__ import unicode_literals
|
|
Packit |
6f3914 |
from dnf.i18n import _
|
|
Packit |
6f3914 |
import logging
|
|
Packit |
6f3914 |
import dnf.pycomp
|
|
Packit |
6f3914 |
import smtplib
|
|
Packit |
6f3914 |
import email.utils
|
|
Packit |
6f3914 |
import subprocess
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
APPLIED = _("The following updates have been applied on '%s':")
|
|
Packit |
6f3914 |
AVAILABLE = _("The following updates are available on '%s':")
|
|
Packit |
6f3914 |
DOWNLOADED = _("The following updates were downloaded on '%s':")
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
logger = logging.getLogger('dnf')
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
class Emitter(object):
|
|
Packit |
6f3914 |
def __init__(self, system_name):
|
|
Packit |
6f3914 |
self._applied = False
|
|
Packit |
6f3914 |
self._available_msg = None
|
|
Packit |
6f3914 |
self._downloaded = False
|
|
Packit |
6f3914 |
self._system_name = system_name
|
|
Packit |
6f3914 |
self._trans_msg = None
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
def _prepare_msg(self):
|
|
Packit |
6f3914 |
msg = []
|
|
Packit |
6f3914 |
if self._applied:
|
|
Packit |
6f3914 |
msg.append(APPLIED % self._system_name)
|
|
Packit |
6f3914 |
msg.append(self._available_msg)
|
|
Packit |
6f3914 |
elif self._downloaded:
|
|
Packit |
6f3914 |
msg.append(DOWNLOADED % self._system_name)
|
|
Packit |
6f3914 |
msg.append(self._available_msg)
|
|
Packit |
6f3914 |
elif self._available_msg:
|
|
Packit |
6f3914 |
msg.append(AVAILABLE % self._system_name)
|
|
Packit |
6f3914 |
msg.append(self._available_msg)
|
|
Packit |
6f3914 |
else:
|
|
Packit |
6f3914 |
return None
|
|
Packit |
6f3914 |
return '\n'.join(msg)
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
def notify_applied(self):
|
|
Packit |
6f3914 |
assert self._available_msg
|
|
Packit |
6f3914 |
self._applied = True
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
def notify_available(self, msg):
|
|
Packit |
6f3914 |
self._available_msg = msg
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
def notify_downloaded(self):
|
|
Packit |
6f3914 |
assert self._available_msg
|
|
Packit |
6f3914 |
self._downloaded = True
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
class EmailEmitter(Emitter):
|
|
Packit |
6f3914 |
def __init__(self, system_name, conf):
|
|
Packit |
6f3914 |
super(EmailEmitter, self).__init__(system_name)
|
|
Packit |
6f3914 |
self._conf = conf
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
def _prepare_msg(self):
|
|
Packit |
6f3914 |
if self._applied:
|
|
Packit |
6f3914 |
subj = _("Updates applied on '%s'.") % self._system_name
|
|
Packit |
6f3914 |
elif self._downloaded:
|
|
Packit |
6f3914 |
subj = _("Updates downloaded on '%s'.") % self._system_name
|
|
Packit |
6f3914 |
elif self._available_msg:
|
|
Packit |
6f3914 |
subj = _("Updates available on '%s'.") % self._system_name
|
|
Packit |
6f3914 |
else:
|
|
Packit |
6f3914 |
return None, None
|
|
Packit |
6f3914 |
return subj, super(EmailEmitter, self)._prepare_msg()
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
def commit(self):
|
|
Packit |
6f3914 |
subj, body = self._prepare_msg()
|
|
Packit |
6f3914 |
message = dnf.pycomp.email_mime(body)
|
|
Packit |
6f3914 |
message.set_charset('utf-8')
|
|
Packit |
6f3914 |
email_from = self._conf.email_from
|
|
Packit |
6f3914 |
email_to = self._conf.email_to
|
|
Packit |
6f3914 |
message['Date'] = email.utils.formatdate()
|
|
Packit |
6f3914 |
message['From'] = email_from
|
|
Packit |
6f3914 |
message['Subject'] = subj
|
|
Packit |
6f3914 |
message['To'] = ','.join(email_to)
|
|
Packit |
6f3914 |
message['Message-ID'] = email.utils.make_msgid()
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
# Send the email
|
|
Packit |
6f3914 |
try:
|
|
Packit |
6f3914 |
smtp = smtplib.SMTP(self._conf.email_host, timeout=300)
|
|
Packit |
6f3914 |
smtp.sendmail(email_from, email_to, message.as_string())
|
|
Packit |
6f3914 |
smtp.close()
|
|
Packit |
6f3914 |
except smtplib.SMTPException as exc:
|
|
Packit |
6f3914 |
msg = _("Failed to send an email via '%s': %s") % (
|
|
Packit |
6f3914 |
self._conf.email_host, exc)
|
|
Packit |
6f3914 |
logger.error(msg)
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
class CommandEmitterMixIn(object):
|
|
Packit |
6f3914 |
"""
|
|
Packit |
6f3914 |
Executes a desired command, and pushes data into its stdin.
|
|
Packit |
6f3914 |
Both data and command can be formatted according to user preference.
|
|
Packit |
6f3914 |
For this reason, this class expects a {str:str} dictionary as _prepare_msg
|
|
Packit |
6f3914 |
return value.
|
|
Packit |
6f3914 |
Meant for mixing with Emitter classes, as it does not define any names used
|
|
Packit |
6f3914 |
for formatting on its own.
|
|
Packit |
6f3914 |
"""
|
|
Packit |
6f3914 |
def commit(self):
|
|
Packit |
6f3914 |
command_fmt = self._conf.command_format
|
|
Packit |
6f3914 |
stdin_fmt = self._conf.stdin_format
|
|
Packit |
6f3914 |
msg = self._prepare_msg()
|
|
Packit |
6f3914 |
# all strings passed to shell should be quoted to avoid accidental code
|
|
Packit |
6f3914 |
# execution
|
|
Packit |
6f3914 |
quoted_msg = dict((key, dnf.pycomp.shlex_quote(val))
|
|
Packit |
6f3914 |
for key, val in msg.items())
|
|
Packit |
6f3914 |
command = command_fmt.format(**quoted_msg)
|
|
Packit |
6f3914 |
stdin_feed = stdin_fmt.format(**msg).encode('utf-8')
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
# Execute the command
|
|
Packit |
6f3914 |
subp = subprocess.Popen(command, shell=True, stdin=subprocess.PIPE)
|
|
Packit |
6f3914 |
subp.communicate(stdin_feed)
|
|
Packit |
6f3914 |
subp.stdin.close()
|
|
Packit |
6f3914 |
if subp.wait() != 0:
|
|
Packit |
6f3914 |
msg = _("Failed to execute command '%s': returned %d") \
|
|
Packit |
6f3914 |
% (command, subp.returncode)
|
|
Packit |
6f3914 |
logger.error(msg)
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
class CommandEmitter(CommandEmitterMixIn, Emitter):
|
|
Packit |
6f3914 |
def _prepare_msg(self):
|
|
Packit |
6f3914 |
return {'body': super(CommandEmitter, self)._prepare_msg()}
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
class CommandEmailEmitter(CommandEmitterMixIn, EmailEmitter):
|
|
Packit |
6f3914 |
def _prepare_msg(self):
|
|
Packit |
6f3914 |
subject, body = super(CommandEmailEmitter, self)._prepare_msg()
|
|
Packit |
6f3914 |
return {'subject': subject,
|
|
Packit |
6f3914 |
'body': body,
|
|
Packit |
6f3914 |
'email_from': self._conf.email_from,
|
|
Packit |
6f3914 |
'email_to': ' '.join(self._conf.email_to)}
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
class StdIoEmitter(Emitter):
|
|
Packit |
6f3914 |
def commit(self):
|
|
Packit |
6f3914 |
msg = self._prepare_msg()
|
|
Packit |
6f3914 |
print(msg)
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
class MotdEmitter(Emitter):
|
|
Packit |
6f3914 |
def commit(self):
|
|
Packit |
6f3914 |
msg = self._prepare_msg()
|
|
Packit |
6f3914 |
with open('/etc/motd', 'w') as fobj:
|
|
Packit |
6f3914 |
fobj.write(msg)
|
|
Packit |
6f3914 |
|