|
|
367f53 |
From a0d7fc6560439fc6de7ae7d246604c7b8f605182 Mon Sep 17 00:00:00 2001
|
|
|
367f53 |
From: Pavel Moravec <pmoravec@redhat.com>
|
|
|
367f53 |
Date: Wed, 6 Oct 2021 14:05:53 +0200
|
|
|
367f53 |
Subject: [PATCH] [Red Hat] Update policy to use SFTP instead of legacy FTP
|
|
|
367f53 |
dropbox
|
|
|
367f53 |
|
|
|
367f53 |
This is a backport of #2552 / #2467 to reflect on python2 systems the
|
|
|
367f53 |
planned FTP dropbox replacement by SFTP system.
|
|
|
367f53 |
|
|
|
367f53 |
Related: #2552
|
|
|
367f53 |
Resolves: #2715
|
|
|
367f53 |
|
|
|
367f53 |
Signed-off-by: Pavel Moravec <pmoravec@redhat.com>
|
|
|
367f53 |
---
|
|
|
367f53 |
man/en/sosreport.1 | 14 +++++
|
|
|
367f53 |
sos/__init__.py | 6 +-
|
|
|
367f53 |
sos/policies/__init__.py | 115 ++++++++++++++++++++++++++++++++++---
|
|
|
367f53 |
sos/policies/redhat.py | 121 ++++++++++++++++++++++++++++-----------
|
|
|
367f53 |
sos/sosreport.py | 3 +
|
|
|
367f53 |
5 files changed, 215 insertions(+), 44 deletions(-)
|
|
|
367f53 |
|
|
|
367f53 |
diff --git a/man/en/sosreport.1 b/man/en/sosreport.1
|
|
|
367f53 |
index 4f7185f1..210f073f 100644
|
|
|
367f53 |
--- a/man/en/sosreport.1
|
|
|
367f53 |
+++ b/man/en/sosreport.1
|
|
|
367f53 |
@@ -32,6 +32,7 @@ sosreport \- Collect and package diagnostic and support data
|
|
|
367f53 |
[--encrypt-pass PASS]\fR
|
|
|
367f53 |
[--upload] [--upload-url url] [--upload-user user]\fR
|
|
|
367f53 |
[--upload-directory dir] [--upload-pass pass]\fR
|
|
|
367f53 |
+ [--upload-protocol protocol]\fR
|
|
|
367f53 |
[--experimental]\fR
|
|
|
367f53 |
[-h|--help]\fR
|
|
|
367f53 |
|
|
|
367f53 |
@@ -314,6 +315,19 @@ when prompted rather than using this option.
|
|
|
367f53 |
Specify a directory to upload to, if one is not specified by a vendor default location
|
|
|
367f53 |
or if your destination server does not allow writes to '/'.
|
|
|
367f53 |
.TP
|
|
|
367f53 |
+.B \--upload-protocol PROTO
|
|
|
367f53 |
+Manually specify the protocol to use for uploading to the target \fBupload-url\fR.
|
|
|
367f53 |
+
|
|
|
367f53 |
+Normally this is determined via the upload address, assuming that the protocol is part
|
|
|
367f53 |
+of the address provided, e.g. 'https://example.com'. By using this option, sos will skip
|
|
|
367f53 |
+the protocol check and use the method defined for the specified PROTO.
|
|
|
367f53 |
+
|
|
|
367f53 |
+For RHEL systems, setting this option to \fBsftp\fR will skip the initial attempt to
|
|
|
367f53 |
+upload to the Red Hat Customer Portal, and only attempt an upload to Red Hat's SFTP server,
|
|
|
367f53 |
+which is typically used as a fallback target.
|
|
|
367f53 |
+
|
|
|
367f53 |
+Valid values for PROTO are: 'auto' (default), 'https', 'ftp', 'sftp'.
|
|
|
367f53 |
+.TP
|
|
|
367f53 |
.B \--experimental
|
|
|
367f53 |
Enable plugins marked as experimental. Experimental plugins may not have
|
|
|
367f53 |
been tested for this port or may still be under active development.
|
|
|
367f53 |
diff --git a/sos/__init__.py b/sos/__init__.py
|
|
|
367f53 |
index 111e066e..b4abf533 100644
|
|
|
367f53 |
--- a/sos/__init__.py
|
|
|
367f53 |
+++ b/sos/__init__.py
|
|
|
367f53 |
@@ -58,7 +58,7 @@ _arg_names = [
|
|
|
367f53 |
'no_postproc', 'note', 'onlyplugins', 'plugin_timeout', 'plugopts',
|
|
|
367f53 |
'preset', 'profiles', 'quiet', 'since', 'sysroot', 'threads', 'tmp_dir',
|
|
|
367f53 |
'upload', 'upload_url', 'upload_directory', 'upload_user', 'upload_pass',
|
|
|
367f53 |
- 'verbosity', 'verify'
|
|
|
367f53 |
+ 'upload_protocol', 'verbosity', 'verify'
|
|
|
367f53 |
]
|
|
|
367f53 |
|
|
|
367f53 |
#: Arguments with non-zero default values
|
|
|
367f53 |
@@ -69,7 +69,8 @@ _arg_defaults = {
|
|
|
367f53 |
"preset": "auto",
|
|
|
367f53 |
# Verbosity has an explicit zero default since the ArgumentParser
|
|
|
367f53 |
# count action default is None.
|
|
|
367f53 |
- "verbosity": 0
|
|
|
367f53 |
+ "verbosity": 0,
|
|
|
367f53 |
+ "upload_protocol": "auto"
|
|
|
367f53 |
}
|
|
|
367f53 |
|
|
|
367f53 |
|
|
|
367f53 |
@@ -200,6 +201,7 @@ class SoSOptions(object):
|
|
|
367f53 |
self.upload_directory = ""
|
|
|
367f53 |
self.upload_user = ""
|
|
|
367f53 |
self.upload_pass = ""
|
|
|
367f53 |
+ self.upload_protocol = _arg_defaults["upload_protocol"]
|
|
|
367f53 |
self.verbosity = _arg_defaults["verbosity"]
|
|
|
367f53 |
self.verify = False
|
|
|
367f53 |
self._nondefault = set()
|
|
|
367f53 |
diff --git a/sos/policies/__init__.py b/sos/policies/__init__.py
|
|
|
367f53 |
index 1579ff5d..acda6379 100644
|
|
|
367f53 |
--- a/sos/policies/__init__.py
|
|
|
367f53 |
+++ b/sos/policies/__init__.py
|
|
|
367f53 |
@@ -15,7 +15,8 @@ from pwd import getpwuid
|
|
|
367f53 |
from sos.utilities import (ImporterHelper,
|
|
|
367f53 |
import_module,
|
|
|
367f53 |
shell_out,
|
|
|
367f53 |
- sos_get_command_output)
|
|
|
367f53 |
+ sos_get_command_output,
|
|
|
367f53 |
+ is_executable)
|
|
|
367f53 |
from sos.plugins import IndependentPlugin, ExperimentalPlugin
|
|
|
367f53 |
from sos import _sos as _
|
|
|
367f53 |
from sos import SoSOptions, _arg_names
|
|
|
367f53 |
@@ -944,6 +945,7 @@ class LinuxPolicy(Policy):
|
|
|
367f53 |
self.upload_user = cmdline_opts.upload_user
|
|
|
367f53 |
self.upload_directory = cmdline_opts.upload_directory
|
|
|
367f53 |
self.upload_password = cmdline_opts.upload_pass
|
|
|
367f53 |
+ self.upload_archive_name = ''
|
|
|
367f53 |
|
|
|
367f53 |
if not cmdline_opts.batch and not \
|
|
|
367f53 |
cmdline_opts.quiet:
|
|
|
367f53 |
@@ -1029,7 +1031,7 @@ class LinuxPolicy(Policy):
|
|
|
367f53 |
this method
|
|
|
367f53 |
|
|
|
367f53 |
"""
|
|
|
367f53 |
- self.upload_archive = archive
|
|
|
367f53 |
+ self.upload_archive_name = archive
|
|
|
367f53 |
if not self.upload_url:
|
|
|
367f53 |
self.upload_url = self.get_upload_url()
|
|
|
367f53 |
if not self.upload_url:
|
|
|
367f53 |
@@ -1051,7 +1053,9 @@ class LinuxPolicy(Policy):
|
|
|
367f53 |
'sftp': self.upload_sftp,
|
|
|
367f53 |
'https': self.upload_https
|
|
|
367f53 |
}
|
|
|
367f53 |
- if '://' not in self.upload_url:
|
|
|
367f53 |
+ if self.commons['cmdlineopts'].upload_protocol in prots.keys():
|
|
|
367f53 |
+ return prots[self.commons['cmdlineopts'].upload_protocol]
|
|
|
367f53 |
+ elif '://' not in self.upload_url:
|
|
|
367f53 |
raise Exception("Must provide protocol in upload URL")
|
|
|
367f53 |
prot, url = self.upload_url.split('://')
|
|
|
367f53 |
if prot not in prots.keys():
|
|
|
367f53 |
@@ -1092,7 +1096,7 @@ class LinuxPolicy(Policy):
|
|
|
367f53 |
"""
|
|
|
367f53 |
return self.upload_password or self._upload_password
|
|
|
367f53 |
|
|
|
367f53 |
- def upload_sftp(self):
|
|
|
367f53 |
+ def upload_sftp(self, user=None, password=None):
|
|
|
367f53 |
"""Attempts to upload the archive to an SFTP location.
|
|
|
367f53 |
|
|
|
367f53 |
Due to the lack of well maintained, secure, and generally widespread
|
|
|
367f53 |
@@ -1102,7 +1106,102 @@ class LinuxPolicy(Policy):
|
|
|
367f53 |
Do not override this method with one that uses python-paramiko, as the
|
|
|
367f53 |
upstream sos team will reject any PR that includes that dependency.
|
|
|
367f53 |
"""
|
|
|
367f53 |
- raise NotImplementedError("SFTP support is not yet implemented")
|
|
|
367f53 |
+ # if we somehow don't have sftp available locally, fail early
|
|
|
367f53 |
+ if not is_executable('sftp'):
|
|
|
367f53 |
+ raise Exception('SFTP is not locally supported')
|
|
|
367f53 |
+
|
|
|
367f53 |
+ # soft dependency on python-pexpect, which we need to use to control
|
|
|
367f53 |
+ # sftp login since as of this writing we don't have a viable solution
|
|
|
367f53 |
+ # via ssh python bindings commonly available among downstreams
|
|
|
367f53 |
+ try:
|
|
|
367f53 |
+ import pexpect
|
|
|
367f53 |
+ except ImportError:
|
|
|
367f53 |
+ raise Exception('SFTP upload requires python-pexpect, which is '
|
|
|
367f53 |
+ 'not currently installed')
|
|
|
367f53 |
+
|
|
|
367f53 |
+ sftp_connected = False
|
|
|
367f53 |
+
|
|
|
367f53 |
+ if not user:
|
|
|
367f53 |
+ user = self.get_upload_user()
|
|
|
367f53 |
+ if not password:
|
|
|
367f53 |
+ password = self.get_upload_password()
|
|
|
367f53 |
+
|
|
|
367f53 |
+ # need to strip the protocol prefix here
|
|
|
367f53 |
+ sftp_url = self.get_upload_url().replace('sftp://', '')
|
|
|
367f53 |
+ sftp_cmd = "sftp -oStrictHostKeyChecking=no %s@%s" % (user, sftp_url)
|
|
|
367f53 |
+ ret = pexpect.spawn(sftp_cmd, encoding='utf-8')
|
|
|
367f53 |
+
|
|
|
367f53 |
+ sftp_expects = [
|
|
|
367f53 |
+ u'sftp>',
|
|
|
367f53 |
+ u'password:',
|
|
|
367f53 |
+ u'Connection refused',
|
|
|
367f53 |
+ pexpect.TIMEOUT,
|
|
|
367f53 |
+ pexpect.EOF
|
|
|
367f53 |
+ ]
|
|
|
367f53 |
+
|
|
|
367f53 |
+ idx = ret.expect(sftp_expects, timeout=15)
|
|
|
367f53 |
+
|
|
|
367f53 |
+ if idx == 0:
|
|
|
367f53 |
+ sftp_connected = True
|
|
|
367f53 |
+ elif idx == 1:
|
|
|
367f53 |
+ ret.sendline(password)
|
|
|
367f53 |
+ pass_expects = [
|
|
|
367f53 |
+ u'sftp>',
|
|
|
367f53 |
+ u'Permission denied',
|
|
|
367f53 |
+ pexpect.TIMEOUT,
|
|
|
367f53 |
+ pexpect.EOF
|
|
|
367f53 |
+ ]
|
|
|
367f53 |
+ sftp_connected = ret.expect(pass_expects, timeout=10) == 0
|
|
|
367f53 |
+ if not sftp_connected:
|
|
|
367f53 |
+ ret.close()
|
|
|
367f53 |
+ raise Exception("Incorrect username or password for %s"
|
|
|
367f53 |
+ % self.get_upload_url_string())
|
|
|
367f53 |
+ elif idx == 2:
|
|
|
367f53 |
+ raise Exception("Connection refused by %s. Incorrect port?"
|
|
|
367f53 |
+ % self.get_upload_url_string())
|
|
|
367f53 |
+ elif idx == 3:
|
|
|
367f53 |
+ raise Exception("Timeout hit trying to connect to %s"
|
|
|
367f53 |
+ % self.get_upload_url_string())
|
|
|
367f53 |
+ elif idx == 4:
|
|
|
367f53 |
+ raise Exception("Unexpected error trying to connect to sftp: %s"
|
|
|
367f53 |
+ % ret.before)
|
|
|
367f53 |
+
|
|
|
367f53 |
+ if not sftp_connected:
|
|
|
367f53 |
+ ret.close()
|
|
|
367f53 |
+ raise Exception("Unable to connect via SFTP to %s"
|
|
|
367f53 |
+ % self.get_upload_url_string())
|
|
|
367f53 |
+
|
|
|
367f53 |
+ put_cmd = 'put %s %s' % (self.upload_archive_name,
|
|
|
367f53 |
+ self._get_sftp_upload_name())
|
|
|
367f53 |
+ ret.sendline(put_cmd)
|
|
|
367f53 |
+
|
|
|
367f53 |
+ put_expects = [
|
|
|
367f53 |
+ u'100%',
|
|
|
367f53 |
+ pexpect.TIMEOUT,
|
|
|
367f53 |
+ pexpect.EOF
|
|
|
367f53 |
+ ]
|
|
|
367f53 |
+
|
|
|
367f53 |
+ put_success = ret.expect(put_expects, timeout=180)
|
|
|
367f53 |
+
|
|
|
367f53 |
+ if put_success == 0:
|
|
|
367f53 |
+ ret.sendline('bye')
|
|
|
367f53 |
+ return True
|
|
|
367f53 |
+ elif put_success == 1:
|
|
|
367f53 |
+ raise Exception("Timeout expired while uploading")
|
|
|
367f53 |
+ elif put_success == 2:
|
|
|
367f53 |
+ raise Exception("Unknown error during upload: %s" % ret.before)
|
|
|
367f53 |
+ else:
|
|
|
367f53 |
+ raise Exception("Unexpected response from server: %s" % ret.before)
|
|
|
367f53 |
+
|
|
|
367f53 |
+ def _get_sftp_upload_name(self):
|
|
|
367f53 |
+ """If a specific file name pattern is required by the SFTP server,
|
|
|
367f53 |
+ override this method in the relevant Policy. Otherwise the archive's
|
|
|
367f53 |
+ name on disk will be used
|
|
|
367f53 |
+
|
|
|
367f53 |
+ :returns: Filename as it will exist on the SFTP server
|
|
|
367f53 |
+ :rtype: ``str``
|
|
|
367f53 |
+ """
|
|
|
367f53 |
+ return self.upload_archive_name.split('/')[-1]
|
|
|
367f53 |
|
|
|
367f53 |
def _upload_https_streaming(self, archive):
|
|
|
367f53 |
"""If upload_https() needs to use requests.put(), this method is used
|
|
|
367f53 |
@@ -1148,7 +1247,7 @@ class LinuxPolicy(Policy):
|
|
|
367f53 |
raise Exception("Unable to upload due to missing python requests "
|
|
|
367f53 |
"library")
|
|
|
367f53 |
|
|
|
367f53 |
- with open(self.upload_archive, 'rb') as arc:
|
|
|
367f53 |
+ with open(self.upload_archive_name, 'rb') as arc:
|
|
|
367f53 |
if not self._use_https_streaming:
|
|
|
367f53 |
r = self._upload_https_no_stream(arc)
|
|
|
367f53 |
else:
|
|
|
367f53 |
@@ -1214,9 +1313,9 @@ class LinuxPolicy(Policy):
|
|
|
367f53 |
% directory)
|
|
|
367f53 |
|
|
|
367f53 |
try:
|
|
|
367f53 |
- with open(self.upload_archive, 'rb') as _arcfile:
|
|
|
367f53 |
+ with open(self.upload_archive_name, 'rb') as _arcfile:
|
|
|
367f53 |
session.storbinary(
|
|
|
367f53 |
- "STOR %s" % self.upload_archive.split('/')[-1],
|
|
|
367f53 |
+ "STOR %s" % self.upload_archive_name.split('/')[-1],
|
|
|
367f53 |
_arcfile
|
|
|
367f53 |
)
|
|
|
367f53 |
session.quit()
|
|
|
367f53 |
diff --git a/sos/policies/redhat.py b/sos/policies/redhat.py
|
|
|
367f53 |
index 3412f445..4da96894 100644
|
|
|
367f53 |
--- a/sos/policies/redhat.py
|
|
|
367f53 |
+++ b/sos/policies/redhat.py
|
|
|
367f53 |
@@ -10,6 +10,7 @@
|
|
|
367f53 |
|
|
|
367f53 |
# This enables the use of with syntax in python 2.5 (e.g. jython)
|
|
|
367f53 |
from __future__ import print_function
|
|
|
367f53 |
+import json
|
|
|
367f53 |
import os
|
|
|
367f53 |
import sys
|
|
|
367f53 |
import re
|
|
|
367f53 |
@@ -19,6 +20,12 @@ from sos.policies import LinuxPolicy, PackageManager, PresetDefaults
|
|
|
367f53 |
from sos import _sos as _
|
|
|
367f53 |
from sos import SoSOptions
|
|
|
367f53 |
|
|
|
367f53 |
+try:
|
|
|
367f53 |
+ import requests
|
|
|
367f53 |
+ REQUESTS_LOADED = True
|
|
|
367f53 |
+except ImportError:
|
|
|
367f53 |
+ REQUESTS_LOADED = False
|
|
|
367f53 |
+
|
|
|
367f53 |
OS_RELEASE = "/etc/os-release"
|
|
|
367f53 |
|
|
|
367f53 |
# In python2.7, input() will not properly return strings, and on python3.x
|
|
|
367f53 |
@@ -44,9 +51,8 @@ class RedHatPolicy(LinuxPolicy):
|
|
|
367f53 |
_host_sysroot = '/'
|
|
|
367f53 |
default_scl_prefix = '/opt/rh'
|
|
|
367f53 |
name_pattern = 'friendly'
|
|
|
367f53 |
- upload_url = 'dropbox.redhat.com'
|
|
|
367f53 |
- upload_user = 'anonymous'
|
|
|
367f53 |
- upload_directory = '/incoming'
|
|
|
367f53 |
+ upload_url = None
|
|
|
367f53 |
+ upload_user = None
|
|
|
367f53 |
|
|
|
367f53 |
def __init__(self, sysroot=None):
|
|
|
367f53 |
super(RedHatPolicy, self).__init__(sysroot=sysroot)
|
|
|
367f53 |
@@ -252,7 +258,7 @@ No changes will be made to system configuration.
|
|
|
367f53 |
"""
|
|
|
367f53 |
|
|
|
367f53 |
RH_API_HOST = "https://access.redhat.com"
|
|
|
367f53 |
-RH_FTP_HOST = "ftp://dropbox.redhat.com"
|
|
|
367f53 |
+RH_SFTP_HOST = "sftp://sftp.access.redhat.com"
|
|
|
367f53 |
|
|
|
367f53 |
|
|
|
367f53 |
class RHELPolicy(RedHatPolicy):
|
|
|
367f53 |
@@ -268,9 +274,7 @@ An archive containing the collected information will be \
|
|
|
367f53 |
generated in %(tmpdir)s and may be provided to a %(vendor)s \
|
|
|
367f53 |
support representative.
|
|
|
367f53 |
""" + disclaimer_text + "%(vendor_text)s\n")
|
|
|
367f53 |
- _upload_url = RH_FTP_HOST
|
|
|
367f53 |
- _upload_user = 'anonymous'
|
|
|
367f53 |
- _upload_directory = '/incoming'
|
|
|
367f53 |
+ _upload_url = RH_SFTP_HOST
|
|
|
367f53 |
|
|
|
367f53 |
def __init__(self, sysroot=None):
|
|
|
367f53 |
super(RHELPolicy, self).__init__(sysroot=sysroot)
|
|
|
367f53 |
@@ -309,33 +313,17 @@ support representative.
|
|
|
367f53 |
return
|
|
|
367f53 |
if self.case_id:
|
|
|
367f53 |
self.upload_user = input(_(
|
|
|
367f53 |
- "Enter your Red Hat Customer Portal username (empty to use "
|
|
|
367f53 |
- "public dropbox): ")
|
|
|
367f53 |
+ "Enter your Red Hat Customer Portal username for uploading ["
|
|
|
367f53 |
+ "empty for anonymous SFTP]: ")
|
|
|
367f53 |
)
|
|
|
367f53 |
- if not self.upload_user:
|
|
|
367f53 |
- self.upload_url = RH_FTP_HOST
|
|
|
367f53 |
- self.upload_user = self._upload_user
|
|
|
367f53 |
-
|
|
|
367f53 |
- def _upload_user_set(self):
|
|
|
367f53 |
- user = self.get_upload_user()
|
|
|
367f53 |
- return user and (user != 'anonymous')
|
|
|
367f53 |
|
|
|
367f53 |
def get_upload_url(self):
|
|
|
367f53 |
if self.upload_url:
|
|
|
367f53 |
return self.upload_url
|
|
|
367f53 |
- if self.commons['cmdlineopts'].upload_url:
|
|
|
367f53 |
+ elif self.commons['cmdlineopts'].upload_url:
|
|
|
367f53 |
return self.commons['cmdlineopts'].upload_url
|
|
|
367f53 |
- # anonymous FTP server should be used as fallback when either:
|
|
|
367f53 |
- # - case id is not set, or
|
|
|
367f53 |
- # - upload user isn't set AND batch mode prevents to prompt for it
|
|
|
367f53 |
- if (not self.case_id) or \
|
|
|
367f53 |
- ((not self._upload_user_set()) and
|
|
|
367f53 |
- self.commons['cmdlineopts'].batch):
|
|
|
367f53 |
- self.upload_user = self._upload_user
|
|
|
367f53 |
- if self.upload_directory is None:
|
|
|
367f53 |
- self.upload_directory = self._upload_directory
|
|
|
367f53 |
- self.upload_password = None
|
|
|
367f53 |
- return RH_FTP_HOST
|
|
|
367f53 |
+ elif self.commons['cmdlineopts'].upload_protocol == 'sftp':
|
|
|
367f53 |
+ return RH_SFTP_HOST
|
|
|
367f53 |
else:
|
|
|
367f53 |
rh_case_api = "/hydra/rest/cases/%s/attachments"
|
|
|
367f53 |
return RH_API_HOST + rh_case_api % self.case_id
|
|
|
367f53 |
@@ -348,13 +336,78 @@ support representative.
|
|
|
367f53 |
def get_upload_url_string(self):
|
|
|
367f53 |
if self.get_upload_url().startswith(RH_API_HOST):
|
|
|
367f53 |
return "Red Hat Customer Portal"
|
|
|
367f53 |
- return self.upload_url or RH_FTP_HOST
|
|
|
367f53 |
+ elif self.get_upload_url().startswith(RH_SFTP_HOST):
|
|
|
367f53 |
+ return "Red Hat Secure FTP"
|
|
|
367f53 |
+ return self.upload_url
|
|
|
367f53 |
|
|
|
367f53 |
- def get_upload_user(self):
|
|
|
367f53 |
- # if this is anything other than dropbox, annonymous won't work
|
|
|
367f53 |
- if self.upload_url != RH_FTP_HOST:
|
|
|
367f53 |
- return self.upload_user
|
|
|
367f53 |
- return self._upload_user
|
|
|
367f53 |
+ def _get_sftp_upload_name(self):
|
|
|
367f53 |
+ """The RH SFTP server will only automatically connect file uploads to
|
|
|
367f53 |
+ cases if the filename _starts_ with the case number
|
|
|
367f53 |
+ """
|
|
|
367f53 |
+ if self.case_id:
|
|
|
367f53 |
+ return "%s_%s" % (self.case_id,
|
|
|
367f53 |
+ self.upload_archive_name.split('/')[-1])
|
|
|
367f53 |
+ return self.upload_archive_name
|
|
|
367f53 |
+
|
|
|
367f53 |
+ def upload_sftp(self):
|
|
|
367f53 |
+ """Override the base upload_sftp to allow for setting an on-demand
|
|
|
367f53 |
+ generated anonymous login for the RH SFTP server if a username and
|
|
|
367f53 |
+ password are not given
|
|
|
367f53 |
+ """
|
|
|
367f53 |
+ if RH_SFTP_HOST.split('//')[1] not in self.get_upload_url():
|
|
|
367f53 |
+ return super(RHELPolicy, self).upload_sftp()
|
|
|
367f53 |
+
|
|
|
367f53 |
+ if not REQUESTS_LOADED:
|
|
|
367f53 |
+ raise Exception("python-requests is not installed and is required"
|
|
|
367f53 |
+ " for obtaining SFTP auth token.")
|
|
|
367f53 |
+ _token = None
|
|
|
367f53 |
+ _user = None
|
|
|
367f53 |
+ # we have a username and password, but we need to reset the password
|
|
|
367f53 |
+ # to be the token returned from the auth endpoint
|
|
|
367f53 |
+ if self.get_upload_user() and self.get_upload_password():
|
|
|
367f53 |
+ url = RH_API_HOST + '/hydra/rest/v1/sftp/token'
|
|
|
367f53 |
+ auth = self.get_upload_https_auth()
|
|
|
367f53 |
+ ret = requests.get(url, auth=auth, timeout=10)
|
|
|
367f53 |
+ if ret.status_code == 200:
|
|
|
367f53 |
+ # credentials are valid
|
|
|
367f53 |
+ _user = self.get_upload_user()
|
|
|
367f53 |
+ _token = json.loads(ret.text)['token']
|
|
|
367f53 |
+ else:
|
|
|
367f53 |
+ print("Unable to retrieve Red Hat auth token using provided "
|
|
|
367f53 |
+ "credentials. Will try anonymous.")
|
|
|
367f53 |
+ # we either do not have a username or password/token, or both
|
|
|
367f53 |
+ if not _token:
|
|
|
367f53 |
+ aurl = RH_API_HOST + '/hydra/rest/v1/sftp/token?isAnonymous=true'
|
|
|
367f53 |
+ anon = requests.get(aurl, timeout=10)
|
|
|
367f53 |
+ if anon.status_code == 200:
|
|
|
367f53 |
+ resp = json.loads(anon.text)
|
|
|
367f53 |
+ _user = resp['username']
|
|
|
367f53 |
+ _token = resp['token']
|
|
|
367f53 |
+ print("Using anonymous user %s for upload. Please inform your "
|
|
|
367f53 |
+ "support engineer." % _user)
|
|
|
367f53 |
+ if _user and _token:
|
|
|
367f53 |
+ return super(RHELPolicy, self).upload_sftp(user=_user,
|
|
|
367f53 |
+ password=_token)
|
|
|
367f53 |
+ raise Exception("Could not retrieve valid or anonymous credentials")
|
|
|
367f53 |
+
|
|
|
367f53 |
+ def upload_archive(self, archive):
|
|
|
367f53 |
+ """Override the base upload_archive to provide for automatic failover
|
|
|
367f53 |
+ from RHCP failures to the public RH dropbox
|
|
|
367f53 |
+ """
|
|
|
367f53 |
+ try:
|
|
|
367f53 |
+ if not self.get_upload_user() or not self.get_upload_password():
|
|
|
367f53 |
+ self.upload_url = RH_SFTP_HOST
|
|
|
367f53 |
+ uploaded = super(RHELPolicy, self).upload_archive(archive)
|
|
|
367f53 |
+ except Exception:
|
|
|
367f53 |
+ uploaded = False
|
|
|
367f53 |
+ if not self.upload_url.startswith(RH_API_HOST):
|
|
|
367f53 |
+ raise
|
|
|
367f53 |
+ else:
|
|
|
367f53 |
+ print("Upload to Red Hat Customer Portal failed. Trying %s"
|
|
|
367f53 |
+ % RH_SFTP_HOST)
|
|
|
367f53 |
+ self.upload_url = RH_SFTP_HOST
|
|
|
367f53 |
+ uploaded = super(RHELPolicy, self).upload_archive(archive)
|
|
|
367f53 |
+ return uploaded
|
|
|
367f53 |
|
|
|
367f53 |
def dist_version(self):
|
|
|
367f53 |
try:
|
|
|
367f53 |
diff --git a/sos/sosreport.py b/sos/sosreport.py
|
|
|
367f53 |
index 5f3fb411..170129ea 100644
|
|
|
367f53 |
--- a/sos/sosreport.py
|
|
|
367f53 |
+++ b/sos/sosreport.py
|
|
|
367f53 |
@@ -255,6 +255,9 @@ def _get_parser():
|
|
|
367f53 |
help="Username to authenticate to upload server with")
|
|
|
367f53 |
parser.add_argument("--upload-pass", default=None,
|
|
|
367f53 |
help="Password to authenticate to upload server with")
|
|
|
367f53 |
+ parser.add_argument("--upload-protocol", default='auto',
|
|
|
367f53 |
+ choices=['auto', 'https', 'ftp', 'sftp'],
|
|
|
367f53 |
+ help="Manually specify the upload protocol")
|
|
|
367f53 |
|
|
|
367f53 |
# Group to make add/del preset exclusive
|
|
|
367f53 |
preset_grp = parser.add_mutually_exclusive_group()
|
|
|
367f53 |
--
|
|
|
367f53 |
2.31.1
|
|
|
367f53 |
|
|
|
367f53 |
From 97de66bc3228b29ff33e5ba67733f15065705d89 Mon Sep 17 00:00:00 2001
|
|
|
367f53 |
From: root <root@bvassova-satellite69.gsslab.brq.redhat.com>
|
|
|
367f53 |
Date: Tue, 23 Nov 2021 11:44:33 +0100
|
|
|
367f53 |
Subject: [PATCH] [redhat] SFTP api change Backporting SFTP related changes
|
|
|
367f53 |
from #2764 and #2772.
|
|
|
367f53 |
|
|
|
367f53 |
Related to: #2764, #2772
|
|
|
367f53 |
Resolves: #2771
|
|
|
367f53 |
|
|
|
367f53 |
Signed-off-by: Barbora Vassova <bvassova@redhat.com>
|
|
|
367f53 |
---
|
|
|
367f53 |
sos/policies/__init__.py | 7 +++++--
|
|
|
367f53 |
sos/policies/redhat.py | 18 +++++++++---------
|
|
|
367f53 |
2 files changed, 14 insertions(+), 11 deletions(-)
|
|
|
367f53 |
|
|
|
367f53 |
diff --git a/sos/policies/__init__.py b/sos/policies/__init__.py
|
|
|
367f53 |
index acda63795..d78e79cfc 100644
|
|
|
367f53 |
--- a/sos/policies/__init__.py
|
|
|
367f53 |
+++ b/sos/policies/__init__.py
|
|
|
367f53 |
@@ -1178,7 +1178,8 @@ def upload_sftp(self, user=None, password=None):
|
|
|
367f53 |
put_expects = [
|
|
|
367f53 |
u'100%',
|
|
|
367f53 |
pexpect.TIMEOUT,
|
|
|
367f53 |
- pexpect.EOF
|
|
|
367f53 |
+ pexpect.EOF,
|
|
|
367f53 |
+ u'No such file or directory'
|
|
|
367f53 |
]
|
|
|
367f53 |
|
|
|
367f53 |
put_success = ret.expect(put_expects, timeout=180)
|
|
|
367f53 |
@@ -1190,6 +1191,8 @@ def upload_sftp(self, user=None, password=None):
|
|
|
367f53 |
raise Exception("Timeout expired while uploading")
|
|
|
367f53 |
elif put_success == 2:
|
|
|
367f53 |
raise Exception("Unknown error during upload: %s" % ret.before)
|
|
|
367f53 |
+ elif put_success == 3:
|
|
|
367f53 |
+ raise Exception("Unable to write archive to destination")
|
|
|
367f53 |
else:
|
|
|
367f53 |
raise Exception("Unexpected response from server: %s" % ret.before)
|
|
|
367f53 |
|
|
|
367f53 |
@@ -1252,7 +1255,7 @@ def upload_https(self):
|
|
|
367f53 |
r = self._upload_https_no_stream(arc)
|
|
|
367f53 |
else:
|
|
|
367f53 |
r = self._upload_https_streaming(arc)
|
|
|
367f53 |
- if r.status_code != 201:
|
|
|
367f53 |
+ if r.status_code != 200 and r.status_code != 201:
|
|
|
367f53 |
if r.status_code == 401:
|
|
|
367f53 |
raise Exception(
|
|
|
367f53 |
"Authentication failed: invalid user credentials"
|
|
|
367f53 |
diff --git a/sos/policies/redhat.py b/sos/policies/redhat.py
|
|
|
367f53 |
index 4da968945..960e7cd2a 100644
|
|
|
367f53 |
--- a/sos/policies/redhat.py
|
|
|
367f53 |
+++ b/sos/policies/redhat.py
|
|
|
367f53 |
@@ -257,7 +257,7 @@ def get_tmp_dir(self, opt_tmp_dir):
|
|
|
367f53 |
No changes will be made to system configuration.
|
|
|
367f53 |
"""
|
|
|
367f53 |
|
|
|
367f53 |
-RH_API_HOST = "https://access.redhat.com"
|
|
|
367f53 |
+RH_API_HOST = "https://api.access.redhat.com"
|
|
|
367f53 |
RH_SFTP_HOST = "sftp://sftp.access.redhat.com"
|
|
|
367f53 |
|
|
|
367f53 |
|
|
|
367f53 |
@@ -325,7 +325,7 @@ def get_upload_url(self):
|
|
|
367f53 |
elif self.commons['cmdlineopts'].upload_protocol == 'sftp':
|
|
|
367f53 |
return RH_SFTP_HOST
|
|
|
367f53 |
else:
|
|
|
367f53 |
- rh_case_api = "/hydra/rest/cases/%s/attachments"
|
|
|
367f53 |
+ rh_case_api = "/support/v1/cases/%s/attachments"
|
|
|
367f53 |
return RH_API_HOST + rh_case_api % self.case_id
|
|
|
367f53 |
|
|
|
367f53 |
def _get_upload_headers(self):
|
|
|
367f53 |
@@ -344,10 +344,10 @@ def _get_sftp_upload_name(self):
|
|
|
367f53 |
"""The RH SFTP server will only automatically connect file uploads to
|
|
|
367f53 |
cases if the filename _starts_ with the case number
|
|
|
367f53 |
"""
|
|
|
367f53 |
+ fname = self.upload_archive_name.split('/')[-1]
|
|
|
367f53 |
if self.case_id:
|
|
|
367f53 |
- return "%s_%s" % (self.case_id,
|
|
|
367f53 |
- self.upload_archive_name.split('/')[-1])
|
|
|
367f53 |
- return self.upload_archive_name
|
|
|
367f53 |
+ return "%s_%s" % (self.case_id, fname)
|
|
|
367f53 |
+ return fname
|
|
|
367f53 |
|
|
|
367f53 |
def upload_sftp(self):
|
|
|
367f53 |
"""Override the base upload_sftp to allow for setting an on-demand
|
|
|
367f53 |
@@ -362,12 +362,12 @@ def upload_sftp(self):
|
|
|
367f53 |
" for obtaining SFTP auth token.")
|
|
|
367f53 |
_token = None
|
|
|
367f53 |
_user = None
|
|
|
367f53 |
+ url = RH_API_HOST + '/support/v2/sftp/token'
|
|
|
367f53 |
# we have a username and password, but we need to reset the password
|
|
|
367f53 |
# to be the token returned from the auth endpoint
|
|
|
367f53 |
if self.get_upload_user() and self.get_upload_password():
|
|
|
367f53 |
- url = RH_API_HOST + '/hydra/rest/v1/sftp/token'
|
|
|
367f53 |
auth = self.get_upload_https_auth()
|
|
|
367f53 |
- ret = requests.get(url, auth=auth, timeout=10)
|
|
|
367f53 |
+ ret = requests.post(url, auth=auth, timeout=10)
|
|
|
367f53 |
if ret.status_code == 200:
|
|
|
367f53 |
# credentials are valid
|
|
|
367f53 |
_user = self.get_upload_user()
|
|
|
367f53 |
@@ -377,8 +377,8 @@ def upload_sftp(self):
|
|
|
367f53 |
"credentials. Will try anonymous.")
|
|
|
367f53 |
# we either do not have a username or password/token, or both
|
|
|
367f53 |
if not _token:
|
|
|
367f53 |
- aurl = RH_API_HOST + '/hydra/rest/v1/sftp/token?isAnonymous=true'
|
|
|
367f53 |
- anon = requests.get(aurl, timeout=10)
|
|
|
367f53 |
+ adata = {"isAnonymous": True}
|
|
|
367f53 |
+ anon = requests.post(url, data=json.dumps(adata), timeout=10)
|
|
|
367f53 |
if anon.status_code == 200:
|
|
|
367f53 |
resp = json.loads(anon.text)
|
|
|
367f53 |
_user = resp['username']
|
|
|
367f53 |
From f231f9e502b2f98910c864c6c9000ae499280051 Mon Sep 17 00:00:00 2001
|
|
|
367f53 |
From: Jake Hunsaker <jhunsake@redhat.com>
|
|
|
367f53 |
Date: Tue, 4 Jan 2022 11:16:57 -0500
|
|
|
367f53 |
Subject: [PATCH] [report] Handle exceptionally old pexpect versions
|
|
|
367f53 |
|
|
|
367f53 |
Depending on configuration, certain downstreams may provide pexpect 2.3
|
|
|
367f53 |
or 4.6 (or later). The backport for SFTP upload support assumed a 4.x
|
|
|
367f53 |
version pexpect, however 2.x does not support the `encoding` parameter
|
|
|
367f53 |
to `pexpect.spawn()`.
|
|
|
367f53 |
|
|
|
367f53 |
Add a version check to determine if that parameter needs to be used or
|
|
|
367f53 |
not. Note that this does not need to be implemented against `main`, as
|
|
|
367f53 |
all supported downstreams for `main` support a minimum version of 4.x.
|
|
|
367f53 |
|
|
|
367f53 |
Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
|
|
|
367f53 |
---
|
|
|
367f53 |
sos/policies/__init__.py | 8 +++++++-
|
|
|
367f53 |
1 file changed, 7 insertions(+), 1 deletion(-)
|
|
|
367f53 |
|
|
|
367f53 |
diff --git a/sos/policies/__init__.py b/sos/policies/__init__.py
|
|
|
367f53 |
index d78e79cfc..562ccad25 100644
|
|
|
367f53 |
--- a/sos/policies/__init__.py
|
|
|
367f53 |
+++ b/sos/policies/__init__.py
|
|
|
367f53 |
@@ -1129,7 +1129,13 @@ def upload_sftp(self, user=None, password=None):
|
|
|
367f53 |
# need to strip the protocol prefix here
|
|
|
367f53 |
sftp_url = self.get_upload_url().replace('sftp://', '')
|
|
|
367f53 |
sftp_cmd = "sftp -oStrictHostKeyChecking=no %s@%s" % (user, sftp_url)
|
|
|
367f53 |
- ret = pexpect.spawn(sftp_cmd, encoding='utf-8')
|
|
|
367f53 |
+
|
|
|
367f53 |
+ if int(pexpect.__version__[0]) >= 4:
|
|
|
367f53 |
+ # newer expect requires decoding from subprocess
|
|
|
367f53 |
+ ret = pexpect.spawn(sftp_cmd, encoding='utf-8')
|
|
|
367f53 |
+ else:
|
|
|
367f53 |
+ # older pexpect does not
|
|
|
367f53 |
+ ret = pexpect.spawn(sftp_cmd)
|
|
|
367f53 |
|
|
|
367f53 |
sftp_expects = [
|
|
|
367f53 |
u'sftp>',
|