747 lines
30 KiB
Diff
747 lines
30 KiB
Diff
|
From 5298080d7360202c72b0af2e24994e4bfad72322 Mon Sep 17 00:00:00 2001
|
||
|
From: Jake Hunsaker <jhunsake@redhat.com>
|
||
|
Date: Fri, 14 May 2021 13:10:04 -0400
|
||
|
Subject: [PATCH] [Policy] Add SFTP upload support
|
||
|
|
||
|
Adds support for uploading via SFTP. This is done via pexpect calling
|
||
|
the system's locally available SFTP binary. If either that binary or
|
||
|
pexpect are unavailable on the local system, we will exit gracefully and
|
||
|
report the issue. This allows sos to keep python3-pexpect as a
|
||
|
recommends rather than a hard dependency.
|
||
|
|
||
|
Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
|
||
|
---
|
||
|
man/en/sos-report.1 | 14 +++++
|
||
|
sos/collector/__init__.py | 7 ++-
|
||
|
sos/policies/distros/__init__.py | 105 +++++++++++++++++++++++++++++--
|
||
|
sos/report/__init__.py | 4 ++
|
||
|
4 files changed, 125 insertions(+), 5 deletions(-)
|
||
|
|
||
|
diff --git a/man/en/sos-report.1 b/man/en/sos-report.1
|
||
|
index c38753d4a..799defafc 100644
|
||
|
--- a/man/en/sos-report.1
|
||
|
+++ b/man/en/sos-report.1
|
||
|
@@ -35,6 +35,7 @@ sosreport \- Collect and package diagnos
|
||
|
[--encrypt-pass PASS]\fR
|
||
|
[--upload] [--upload-url url] [--upload-user user]\fR
|
||
|
[--upload-directory dir] [--upload-pass pass]\fR
|
||
|
+ [--upload-protocol protocol]\fR
|
||
|
[--experimental]\fR
|
||
|
[-h|--help]\fR
|
||
|
|
||
|
@@ -354,6 +355,19 @@ be used provided all other required valu
|
||
|
Specify a directory to upload to, if one is not specified by a vendor default location
|
||
|
or if your destination server does not allow writes to '/'.
|
||
|
.TP
|
||
|
+.B \--upload-protocol PROTO
|
||
|
+Manually specify the protocol to use for uploading to the target \fBupload-url\fR.
|
||
|
+
|
||
|
+Normally this is determined via the upload address, assuming that the protocol is part
|
||
|
+of the address provided, e.g. 'https://example.com'. By using this option, sos will skip
|
||
|
+the protocol check and use the method defined for the specified PROTO.
|
||
|
+
|
||
|
+For RHEL systems, setting this option to \fBsftp\fR will skip the initial attempt to
|
||
|
+upload to the Red Hat Customer Portal, and only attempt an upload to Red Hat's SFTP server,
|
||
|
+which is typically used as a fallback target.
|
||
|
+
|
||
|
+Valid values for PROTO are: 'auto' (default), 'https', 'ftp', 'sftp'.
|
||
|
+.TP
|
||
|
.B \--experimental
|
||
|
Enable plugins marked as experimental. Experimental plugins may not have
|
||
|
been tested for this port or may still be under active development.
|
||
|
diff --git a/sos/collector/__init__.py b/sos/collector/__init__.py
|
||
|
index 5d1c599ac..1c742cf50 100644
|
||
|
--- a/sos/collector/__init__.py
|
||
|
+++ b/sos/collector/__init__.py
|
||
|
@@ -106,6 +106,7 @@ class SoSCollector(SoSComponent):
|
||
|
'upload_directory': None,
|
||
|
'upload_user': None,
|
||
|
'upload_pass': None,
|
||
|
+ 'upload_protocol': 'auto'
|
||
|
}
|
||
|
|
||
|
def __init__(self, parser, parsed_args, cmdline_args):
|
||
|
@@ -383,6 +384,9 @@ class SoSCollector(SoSComponent):
|
||
|
help="Username to authenticate with")
|
||
|
collect_grp.add_argument("--upload-pass", default=None,
|
||
|
help="Password to authenticate with")
|
||
|
+ collect_grp.add_argument("--upload-protocol", default='auto',
|
||
|
+ choices=['auto', 'https', 'ftp', 'sftp'],
|
||
|
+ help="Manually specify the upload protocol")
|
||
|
|
||
|
# Group the cleaner options together
|
||
|
cleaner_grp = parser.add_argument_group(
|
||
|
diff --git a/sos/policies/distros/__init__.py b/sos/policies/distros/__init__.py
|
||
|
index 4268688c6..9fe31513b 100644
|
||
|
--- a/sos/policies/distros/__init__.py
|
||
|
+++ b/sos/policies/distros/__init__.py
|
||
|
@@ -20,7 +20,7 @@
|
||
|
from sos.policies.runtimes.podman import PodmanContainerRuntime
|
||
|
from sos.policies.runtimes.docker import DockerContainerRuntime
|
||
|
|
||
|
-from sos.utilities import shell_out
|
||
|
+from sos.utilities import shell_out, is_executable
|
||
|
|
||
|
|
||
|
try:
|
||
|
@@ -295,7 +295,9 @@ def _determine_upload_type(self):
|
||
|
'sftp': self.upload_sftp,
|
||
|
'https': self.upload_https
|
||
|
}
|
||
|
- if '://' not in self.upload_url:
|
||
|
+ if self.commons['cmdlineopts'].upload_protocol in prots.keys():
|
||
|
+ return prots[self.commons['cmdlineopts'].upload_protocol]
|
||
|
+ elif '://' not in self.upload_url:
|
||
|
raise Exception("Must provide protocol in upload URL")
|
||
|
prot, url = self.upload_url.split('://')
|
||
|
if prot not in prots.keys():
|
||
|
@@ -361,7 +363,7 @@ def get_upload_password(self):
|
||
|
self.upload_password or
|
||
|
self._upload_password)
|
||
|
|
||
|
- def upload_sftp(self):
|
||
|
+ def upload_sftp(self, user=None, password=None):
|
||
|
"""Attempts to upload the archive to an SFTP location.
|
||
|
|
||
|
Due to the lack of well maintained, secure, and generally widespread
|
||
|
@@ -371,7 +373,102 @@ def upload_sftp(self):
|
||
|
Do not override this method with one that uses python-paramiko, as the
|
||
|
upstream sos team will reject any PR that includes that dependency.
|
||
|
"""
|
||
|
- raise NotImplementedError("SFTP support is not yet implemented")
|
||
|
+ # if we somehow don't have sftp available locally, fail early
|
||
|
+ if not is_executable('sftp'):
|
||
|
+ raise Exception('SFTP is not locally supported')
|
||
|
+
|
||
|
+ # soft dependency on python3-pexpect, which we need to use to control
|
||
|
+ # sftp login since as of this writing we don't have a viable solution
|
||
|
+ # via ssh python bindings commonly available among downstreams
|
||
|
+ try:
|
||
|
+ import pexpect
|
||
|
+ except ImportError:
|
||
|
+ raise Exception('SFTP upload requires python3-pexpect, which is '
|
||
|
+ 'not currently installed')
|
||
|
+
|
||
|
+ sftp_connected = False
|
||
|
+
|
||
|
+ if not user:
|
||
|
+ user = self.get_upload_user()
|
||
|
+ if not password:
|
||
|
+ password = self.get_upload_password()
|
||
|
+
|
||
|
+ # need to strip the protocol prefix here
|
||
|
+ sftp_url = self.get_upload_url().replace('sftp://', '')
|
||
|
+ sftp_cmd = "sftp -oStrictHostKeyChecking=no %s@%s" % (user, sftp_url)
|
||
|
+ ret = pexpect.spawn(sftp_cmd, encoding='utf-8')
|
||
|
+
|
||
|
+ sftp_expects = [
|
||
|
+ u'sftp>',
|
||
|
+ u'password:',
|
||
|
+ u'Connection refused',
|
||
|
+ pexpect.TIMEOUT,
|
||
|
+ pexpect.EOF
|
||
|
+ ]
|
||
|
+
|
||
|
+ idx = ret.expect(sftp_expects, timeout=15)
|
||
|
+
|
||
|
+ if idx == 0:
|
||
|
+ sftp_connected = True
|
||
|
+ elif idx == 1:
|
||
|
+ ret.sendline(password)
|
||
|
+ pass_expects = [
|
||
|
+ u'sftp>',
|
||
|
+ u'Permission denied',
|
||
|
+ pexpect.TIMEOUT,
|
||
|
+ pexpect.EOF
|
||
|
+ ]
|
||
|
+ sftp_connected = ret.expect(pass_expects, timeout=10) == 0
|
||
|
+ if not sftp_connected:
|
||
|
+ ret.close()
|
||
|
+ raise Exception("Incorrect username or password for %s"
|
||
|
+ % self.get_upload_url_string())
|
||
|
+ elif idx == 2:
|
||
|
+ raise Exception("Connection refused by %s. Incorrect port?"
|
||
|
+ % self.get_upload_url_string())
|
||
|
+ elif idx == 3:
|
||
|
+ raise Exception("Timeout hit trying to connect to %s"
|
||
|
+ % self.get_upload_url_string())
|
||
|
+ elif idx == 4:
|
||
|
+ raise Exception("Unexpected error trying to connect to sftp: %s"
|
||
|
+ % ret.before)
|
||
|
+
|
||
|
+ if not sftp_connected:
|
||
|
+ ret.close()
|
||
|
+ raise Exception("Unable to connect via SFTP to %s"
|
||
|
+ % self.get_upload_url_string())
|
||
|
+
|
||
|
+ put_cmd = 'put %s %s' % (self.upload_archive_name,
|
||
|
+ self._get_sftp_upload_name())
|
||
|
+ ret.sendline(put_cmd)
|
||
|
+
|
||
|
+ put_expects = [
|
||
|
+ u'100%',
|
||
|
+ pexpect.TIMEOUT,
|
||
|
+ pexpect.EOF
|
||
|
+ ]
|
||
|
+
|
||
|
+ put_success = ret.expect(put_expects, timeout=180)
|
||
|
+
|
||
|
+ if put_success == 0:
|
||
|
+ ret.sendline('bye')
|
||
|
+ return True
|
||
|
+ elif put_success == 1:
|
||
|
+ raise Exception("Timeout expired while uploading")
|
||
|
+ elif put_success == 2:
|
||
|
+ raise Exception("Unknown error during upload: %s" % ret.before)
|
||
|
+ else:
|
||
|
+ raise Exception("Unexpected response from server: %s" % ret.before)
|
||
|
+
|
||
|
+ def _get_sftp_upload_name(self):
|
||
|
+ """If a specific file name pattern is required by the SFTP server,
|
||
|
+ override this method in the relevant Policy. Otherwise the archive's
|
||
|
+ name on disk will be used
|
||
|
+
|
||
|
+ :returns: Filename as it will exist on the SFTP server
|
||
|
+ :rtype: ``str``
|
||
|
+ """
|
||
|
+ return self.upload_archive_name.split('/')[-1]
|
||
|
|
||
|
def _upload_https_streaming(self, archive):
|
||
|
"""If upload_https() needs to use requests.put(), this method is used
|
||
|
diff --git a/sos/report/__init__.py b/sos/report/__init__.py
|
||
|
index df99186db..d43454092 100644
|
||
|
--- a/sos/report/__init__.py
|
||
|
+++ b/sos/report/__init__.py
|
||
|
@@ -119,6 +119,7 @@ class SoSReport(SoSComponent):
|
||
|
'upload_directory': None,
|
||
|
'upload_user': None,
|
||
|
'upload_pass': None,
|
||
|
+ 'upload_protocol': 'auto',
|
||
|
'add_preset': '',
|
||
|
'del_preset': ''
|
||
|
}
|
||
|
@@ -300,6 +301,9 @@ class SoSReport(SoSComponent):
|
||
|
help="Username to authenticate to server with")
|
||
|
report_grp.add_argument("--upload-pass", default=None,
|
||
|
help="Password to authenticate to server with")
|
||
|
+ report_grp.add_argument("--upload-protocol", default='auto',
|
||
|
+ choices=['auto', 'https', 'ftp', 'sftp'],
|
||
|
+ help="Manually specify the upload protocol")
|
||
|
|
||
|
# Group to make add/del preset exclusive
|
||
|
preset_grp = report_grp.add_mutually_exclusive_group()
|
||
|
From d5316de87313c3eaf9fe4ce7a5eea3ed8c7d17ce Mon Sep 17 00:00:00 2001
|
||
|
From: Jake Hunsaker <jhunsake@redhat.com>
|
||
|
Date: Fri, 14 May 2021 13:11:27 -0400
|
||
|
Subject: [PATCH] [Red Hat] Update policy to use SFTP instead of legacy FTP
|
||
|
dropbox
|
||
|
|
||
|
As the FTP dropbox for Red Hat is being decomissioned and replaced with
|
||
|
an SFTP alternative, update the policy to fallback to the SFTP host and
|
||
|
remove legacy FTP host references.
|
||
|
|
||
|
The default behavior for --upload remains the same, just targeting a
|
||
|
different location. If a username, password, and case number are given,
|
||
|
the first attempt will be to upload to the Red Hat Customer Portal. If
|
||
|
any are missing, or are invalid, then we will fallback to SFTP. During
|
||
|
the fallback if a valid username and password are not provided, sos will
|
||
|
attempt to obtain an anonymous token for the upload before failing out
|
||
|
entirely.
|
||
|
|
||
|
Closes: #2467
|
||
|
Resolves: #2552
|
||
|
|
||
|
Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
|
||
|
---
|
||
|
sos/policies/distros/redhat.py | 115 ++++++++++++++++++++++-----------
|
||
|
1 file changed, 76 insertions(+), 39 deletions(-)
|
||
|
|
||
|
diff --git a/sos/policies/distros/redhat.py b/sos/policies/distros/redhat.py
|
||
|
index f37519910..241d3f139 100644
|
||
|
--- a/sos/policies/distros/redhat.py
|
||
|
+++ b/sos/policies/distros/redhat.py
|
||
|
@@ -8,6 +8,7 @@
|
||
|
#
|
||
|
# See the LICENSE file in the source distribution for further information.
|
||
|
|
||
|
+import json
|
||
|
import os
|
||
|
import sys
|
||
|
import re
|
||
|
@@ -20,6 +21,11 @@
|
||
|
from sos.policies.package_managers.rpm import RpmPackageManager
|
||
|
from sos import _sos as _
|
||
|
|
||
|
+try:
|
||
|
+ import requests
|
||
|
+ REQUESTS_LOADED = True
|
||
|
+except ImportError:
|
||
|
+ REQUESTS_LOADED = False
|
||
|
|
||
|
OS_RELEASE = "/etc/os-release"
|
||
|
RHEL_RELEASE_STR = "Red Hat Enterprise Linux"
|
||
|
@@ -39,9 +45,8 @@ class RedHatPolicy(LinuxPolicy):
|
||
|
_host_sysroot = '/'
|
||
|
default_scl_prefix = '/opt/rh'
|
||
|
name_pattern = 'friendly'
|
||
|
- upload_url = 'dropbox.redhat.com'
|
||
|
- upload_user = 'anonymous'
|
||
|
- upload_directory = '/incoming'
|
||
|
+ upload_url = None
|
||
|
+ upload_user = None
|
||
|
default_container_runtime = 'podman'
|
||
|
sos_pkg_name = 'sos'
|
||
|
sos_bin_path = '/usr/sbin'
|
||
|
@@ -196,7 +201,7 @@ def get_tmp_dir(self, opt_tmp_dir):
|
||
|
"""
|
||
|
|
||
|
RH_API_HOST = "https://access.redhat.com"
|
||
|
-RH_FTP_HOST = "ftp://dropbox.redhat.com"
|
||
|
+RH_SFTP_HOST = "sftp://sftp.access.redhat.com"
|
||
|
|
||
|
|
||
|
class RHELPolicy(RedHatPolicy):
|
||
|
@@ -216,9 +216,7 @@ An archive containing the collected info
|
||
|
generated in %(tmpdir)s and may be provided to a %(vendor)s \
|
||
|
support representative.
|
||
|
""" + disclaimer_text + "%(vendor_text)s\n")
|
||
|
- _upload_url = RH_FTP_HOST
|
||
|
- _upload_user = 'anonymous'
|
||
|
- _upload_directory = '/incoming'
|
||
|
+ _upload_url = RH_SFTP_HOST
|
||
|
|
||
|
def __init__(self, sysroot=None, init=None, probe_runtime=True,
|
||
|
remote_exec=None):
|
||
|
@@ -260,33 +263,17 @@ def prompt_for_upload_user(self):
|
||
|
return
|
||
|
if self.case_id and not self.get_upload_user():
|
||
|
self.upload_user = input(_(
|
||
|
- "Enter your Red Hat Customer Portal username (empty to use "
|
||
|
- "public dropbox): ")
|
||
|
+ "Enter your Red Hat Customer Portal username for uploading ["
|
||
|
+ "empty for anonymous SFTP]: ")
|
||
|
)
|
||
|
- if not self.upload_user:
|
||
|
- self.upload_url = RH_FTP_HOST
|
||
|
- self.upload_user = self._upload_user
|
||
|
-
|
||
|
- def _upload_user_set(self):
|
||
|
- user = self.get_upload_user()
|
||
|
- return user and (user != 'anonymous')
|
||
|
|
||
|
def get_upload_url(self):
|
||
|
if self.upload_url:
|
||
|
return self.upload_url
|
||
|
- if self.commons['cmdlineopts'].upload_url:
|
||
|
+ elif self.commons['cmdlineopts'].upload_url:
|
||
|
return self.commons['cmdlineopts'].upload_url
|
||
|
- # anonymous FTP server should be used as fallback when either:
|
||
|
- # - case id is not set, or
|
||
|
- # - upload user isn't set AND batch mode prevents to prompt for it
|
||
|
- if (not self.case_id) or \
|
||
|
- ((not self._upload_user_set()) and
|
||
|
- self.commons['cmdlineopts'].batch):
|
||
|
- self.upload_user = self._upload_user
|
||
|
- if self.upload_directory is None:
|
||
|
- self.upload_directory = self._upload_directory
|
||
|
- self.upload_password = None
|
||
|
- return RH_FTP_HOST
|
||
|
+ elif self.commons['cmdlineopts'].upload_protocol == 'sftp':
|
||
|
+ return RH_SFTP_HOST
|
||
|
else:
|
||
|
rh_case_api = "/hydra/rest/cases/%s/attachments"
|
||
|
return RH_API_HOST + rh_case_api % self.case_id
|
||
|
@@ -299,27 +286,77 @@ def _get_upload_headers(self):
|
||
|
def get_upload_url_string(self):
|
||
|
if self.get_upload_url().startswith(RH_API_HOST):
|
||
|
return "Red Hat Customer Portal"
|
||
|
- return self.upload_url or RH_FTP_HOST
|
||
|
+ elif self.get_upload_url().startswith(RH_SFTP_HOST):
|
||
|
+ return "Red Hat Secure FTP"
|
||
|
+ return self.upload_url
|
||
|
|
||
|
- def get_upload_user(self):
|
||
|
- # if this is anything other than dropbox, annonymous won't work
|
||
|
- if self.upload_url != RH_FTP_HOST:
|
||
|
- return os.getenv('SOSUPLOADUSER', None) or self.upload_user
|
||
|
- return self._upload_user
|
||
|
+ def _get_sftp_upload_name(self):
|
||
|
+ """The RH SFTP server will only automatically connect file uploads to
|
||
|
+ cases if the filename _starts_ with the case number
|
||
|
+ """
|
||
|
+ if self.case_id:
|
||
|
+ return "%s_%s" % (self.case_id,
|
||
|
+ self.upload_archive_name.split('/')[-1])
|
||
|
+ return self.upload_archive_name
|
||
|
+
|
||
|
+ def upload_sftp(self):
|
||
|
+ """Override the base upload_sftp to allow for setting an on-demand
|
||
|
+ generated anonymous login for the RH SFTP server if a username and
|
||
|
+ password are not given
|
||
|
+ """
|
||
|
+ if RH_SFTP_HOST.split('//')[1] not in self.get_upload_url():
|
||
|
+ return super(RHELPolicy, self).upload_sftp()
|
||
|
+
|
||
|
+ if not REQUESTS_LOADED:
|
||
|
+ raise Exception("python3-requests is not installed and is required"
|
||
|
+ " for obtaining SFTP auth token.")
|
||
|
+ _token = None
|
||
|
+ _user = None
|
||
|
+ # we have a username and password, but we need to reset the password
|
||
|
+ # to be the token returned from the auth endpoint
|
||
|
+ if self.get_upload_user() and self.get_upload_password():
|
||
|
+ url = RH_API_HOST + '/hydra/rest/v1/sftp/token'
|
||
|
+ auth = self.get_upload_https_auth()
|
||
|
+ ret = requests.get(url, auth=auth, timeout=10)
|
||
|
+ if ret.status_code == 200:
|
||
|
+ # credentials are valid
|
||
|
+ _user = self.get_upload_user()
|
||
|
+ _token = json.loads(ret.text)['token']
|
||
|
+ else:
|
||
|
+ print("Unable to retrieve Red Hat auth token using provided "
|
||
|
+ "credentials. Will try anonymous.")
|
||
|
+ # we either do not have a username or password/token, or both
|
||
|
+ if not _token:
|
||
|
+ aurl = RH_API_HOST + '/hydra/rest/v1/sftp/token?isAnonymous=true'
|
||
|
+ anon = requests.get(aurl, timeout=10)
|
||
|
+ if anon.status_code == 200:
|
||
|
+ resp = json.loads(anon.text)
|
||
|
+ _user = resp['username']
|
||
|
+ _token = resp['token']
|
||
|
+ print("Using anonymous user %s for upload. Please inform your "
|
||
|
+ "support engineer." % _user)
|
||
|
+ if _user and _token:
|
||
|
+ return super(RHELPolicy, self).upload_sftp(user=_user,
|
||
|
+ password=_token)
|
||
|
+ raise Exception("Could not retrieve valid or anonymous credentials")
|
||
|
|
||
|
def upload_archive(self, archive):
|
||
|
"""Override the base upload_archive to provide for automatic failover
|
||
|
from RHCP failures to the public RH dropbox
|
||
|
"""
|
||
|
try:
|
||
|
+ if not self.get_upload_user() or not self.get_upload_password():
|
||
|
+ self.upload_url = RH_SFTP_HOST
|
||
|
uploaded = super(RHELPolicy, self).upload_archive(archive)
|
||
|
except Exception:
|
||
|
uploaded = False
|
||
|
- if not uploaded and self.upload_url.startswith(RH_API_HOST):
|
||
|
- print("Upload to Red Hat Customer Portal failed. Trying %s"
|
||
|
- % RH_FTP_HOST)
|
||
|
- self.upload_url = RH_FTP_HOST
|
||
|
- uploaded = super(RHELPolicy, self).upload_archive(archive)
|
||
|
+ if not self.upload_url.startswith(RH_API_HOST):
|
||
|
+ raise
|
||
|
+ else:
|
||
|
+ print("Upload to Red Hat Customer Portal failed. Trying %s"
|
||
|
+ % RH_SFTP_HOST)
|
||
|
+ self.upload_url = RH_SFTP_HOST
|
||
|
+ uploaded = super(RHELPolicy, self).upload_archive(archive)
|
||
|
return uploaded
|
||
|
|
||
|
def dist_version(self):
|
||
|
From 8a7ae6a3ac69a020758f7b0825a872e44714dbed Mon Sep 17 00:00:00 2001
|
||
|
From: Jake Hunsaker <jhunsake@redhat.com>
|
||
|
Date: Fri, 9 Apr 2021 11:05:47 -0400
|
||
|
Subject: [PATCH] [ubuntu|Policy] Fix exception when attempting upload
|
||
|
|
||
|
Fixes an issue where an upload attempt on Ubuntu systems would fail with
|
||
|
a traceback due to the upload archive's name not being available when we
|
||
|
first check to make sure we have an upload location, since the Ubuntu
|
||
|
default location requires an archive/file name.
|
||
|
|
||
|
Related to this, stop clobbering `upload_archive` and assign the archive
|
||
|
name to an `upload_archive_name` variable instead.
|
||
|
|
||
|
Closes: #2472
|
||
|
Resolves: #2479
|
||
|
|
||
|
Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
|
||
|
---
|
||
|
sos/policies/distros/__init__.py | 9 +++++----
|
||
|
sos/policies/distros/ubuntu.py | 4 +++-
|
||
|
2 files changed, 8 insertions(+), 5 deletions(-)
|
||
|
|
||
|
diff --git a/sos/policies/distros/__init__.py b/sos/policies/distros/__init__.py
|
||
|
index 022ba7f4c6..a24a0e5beb 100644
|
||
|
--- a/sos/policies/distros/__init__.py
|
||
|
+++ b/sos/policies/distros/__init__.py
|
||
|
@@ -149,6 +149,7 @@ def pre_work(self):
|
||
|
self.upload_user = cmdline_opts.upload_user
|
||
|
self.upload_directory = cmdline_opts.upload_directory
|
||
|
self.upload_password = cmdline_opts.upload_pass
|
||
|
+ self.upload_archive_name = ''
|
||
|
|
||
|
if not cmdline_opts.batch and not \
|
||
|
cmdline_opts.quiet:
|
||
|
@@ -237,7 +238,7 @@ def upload_archive(self, archive):
|
||
|
`get_upload_url_string()`
|
||
|
Print a more human-friendly string than vendor URLs
|
||
|
"""
|
||
|
- self.upload_archive = archive
|
||
|
+ self.upload_archive_name = archive
|
||
|
if not self.upload_url:
|
||
|
self.upload_url = self.get_upload_url()
|
||
|
if not self.upload_url:
|
||
|
@@ -384,7 +385,7 @@ def upload_https(self):
|
||
|
raise Exception("Unable to upload due to missing python requests "
|
||
|
"library")
|
||
|
|
||
|
- with open(self.upload_archive, 'rb') as arc:
|
||
|
+ with open(self.upload_archive_name, 'rb') as arc:
|
||
|
if not self._use_https_streaming:
|
||
|
r = self._upload_https_no_stream(arc)
|
||
|
else:
|
||
|
@@ -467,9 +468,9 @@ def upload_ftp(self, url=None, directory=None, user=None, password=None):
|
||
|
% str(err))
|
||
|
|
||
|
try:
|
||
|
- with open(self.upload_archive, 'rb') as _arcfile:
|
||
|
+ with open(self.upload_archive_name, 'rb') as _arcfile:
|
||
|
session.storbinary(
|
||
|
- "STOR %s" % self.upload_archive.split('/')[-1],
|
||
|
+ "STOR %s" % self.upload_archive_name.split('/')[-1],
|
||
|
_arcfile
|
||
|
)
|
||
|
session.quit()
|
||
|
diff --git a/sos/policies/distros/ubuntu.py b/sos/policies/distros/ubuntu.py
|
||
|
index 94a4a241b0..308c1e3544 100644
|
||
|
--- a/sos/policies/distros/ubuntu.py
|
||
|
+++ b/sos/policies/distros/ubuntu.py
|
||
|
@@ -74,7 +74,9 @@ def get_upload_url_string(self):
|
||
|
|
||
|
def get_upload_url(self):
|
||
|
if not self.upload_url or self.upload_url.startswith(self._upload_url):
|
||
|
- fname = os.path.basename(self.upload_archive)
|
||
|
+ if not self.upload_archive_name:
|
||
|
+ return self._upload_url
|
||
|
+ fname = os.path.basename(self.upload_archive_name)
|
||
|
return self._upload_url + fname
|
||
|
super(UbuntuPolicy, self).get_upload_url()
|
||
|
|
||
|
From 2e8b5e2d4f30854cce93d149fc7d24b9d9cfd02c Mon Sep 17 00:00:00 2001
|
||
|
From: Pavel Moravec <pmoravec@redhat.com>
|
||
|
Date: Fri, 19 Nov 2021 16:16:07 +0100
|
||
|
Subject: [PATCH 1/3] [policies] strip path from SFTP upload filename
|
||
|
|
||
|
When case_id is not supplied, we ask SFTP server to store the uploaded
|
||
|
file under name /var/tmp/<tarball>, which is confusing.
|
||
|
|
||
|
Let remove the path from it also in case_id not supplied.
|
||
|
|
||
|
Related to: #2764
|
||
|
|
||
|
Signed-off-by: Pavel Moravec <pmoravec@redhat.com>
|
||
|
---
|
||
|
sos/policies/distros/redhat.py | 6 +++---
|
||
|
1 file changed, 3 insertions(+), 3 deletions(-)
|
||
|
|
||
|
diff --git a/sos/policies/distros/redhat.py b/sos/policies/distros/redhat.py
|
||
|
index 3476e21fb..8817fc785 100644
|
||
|
--- a/sos/policies/distros/redhat.py
|
||
|
+++ b/sos/policies/distros/redhat.py
|
||
|
@@ -269,10 +269,10 @@ def _get_sftp_upload_name(self):
|
||
|
"""The RH SFTP server will only automatically connect file uploads to
|
||
|
cases if the filename _starts_ with the case number
|
||
|
"""
|
||
|
+ fname = self.upload_archive_name.split('/')[-1]
|
||
|
if self.case_id:
|
||
|
- return "%s_%s" % (self.case_id,
|
||
|
- self.upload_archive_name.split('/')[-1])
|
||
|
- return self.upload_archive_name
|
||
|
+ return "%s_%s" % (self.case_id, fname)
|
||
|
+ return fname
|
||
|
|
||
|
def upload_sftp(self):
|
||
|
"""Override the base upload_sftp to allow for setting an on-demand
|
||
|
|
||
|
From 61023b29a656dd7afaa4a0643368b0a53f1a3779 Mon Sep 17 00:00:00 2001
|
||
|
From: Pavel Moravec <pmoravec@redhat.com>
|
||
|
Date: Fri, 19 Nov 2021 17:31:31 +0100
|
||
|
Subject: [PATCH 2/3] [redhat] update SFTP API version to v2
|
||
|
|
||
|
Change API version from v1 to v2, which includes:
|
||
|
- change of URL
|
||
|
- different URI
|
||
|
- POST method for token generation instead of GET
|
||
|
|
||
|
Resolves: #2764
|
||
|
|
||
|
Signed-off-by: Pavel Moravec <pmoravec@redhat.com>
|
||
|
---
|
||
|
sos/policies/distros/redhat.py | 10 +++++-----
|
||
|
1 file changed, 5 insertions(+), 5 deletions(-)
|
||
|
|
||
|
diff --git a/sos/policies/distros/redhat.py b/sos/policies/distros/redhat.py
|
||
|
index 8817fc785..e4e2b8835 100644
|
||
|
--- a/sos/policies/distros/redhat.py
|
||
|
+++ b/sos/policies/distros/redhat.py
|
||
|
@@ -175,7 +175,7 @@ def get_tmp_dir(self, opt_tmp_dir):
|
||
|
No changes will be made to system configuration.
|
||
|
"""
|
||
|
|
||
|
-RH_API_HOST = "https://access.redhat.com"
|
||
|
+RH_API_HOST = "https://api.access.redhat.com"
|
||
|
RH_SFTP_HOST = "sftp://sftp.access.redhat.com"
|
||
|
|
||
|
|
||
|
@@ -287,12 +287,12 @@ def upload_sftp(self):
|
||
|
" for obtaining SFTP auth token.")
|
||
|
_token = None
|
||
|
_user = None
|
||
|
+ url = RH_API_HOST + '/support/v2/sftp/token'
|
||
|
# we have a username and password, but we need to reset the password
|
||
|
# to be the token returned from the auth endpoint
|
||
|
if self.get_upload_user() and self.get_upload_password():
|
||
|
- url = RH_API_HOST + '/hydra/rest/v1/sftp/token'
|
||
|
auth = self.get_upload_https_auth()
|
||
|
- ret = requests.get(url, auth=auth, timeout=10)
|
||
|
+ ret = requests.post(url, auth=auth, timeout=10)
|
||
|
if ret.status_code == 200:
|
||
|
# credentials are valid
|
||
|
_user = self.get_upload_user()
|
||
|
@@ -302,8 +302,8 @@ def upload_sftp(self):
|
||
|
"credentials. Will try anonymous.")
|
||
|
# we either do not have a username or password/token, or both
|
||
|
if not _token:
|
||
|
- aurl = RH_API_HOST + '/hydra/rest/v1/sftp/token?isAnonymous=true'
|
||
|
- anon = requests.get(aurl, timeout=10)
|
||
|
+ adata = {"isAnonymous": True}
|
||
|
+ anon = requests.post(url, data=json.dumps(adata), timeout=10)
|
||
|
if anon.status_code == 200:
|
||
|
resp = json.loads(anon.text)
|
||
|
_user = resp['username']
|
||
|
|
||
|
From 267da2156ec61f526dd28e760ff6528408a76c3f Mon Sep 17 00:00:00 2001
|
||
|
From: Pavel Moravec <pmoravec@redhat.com>
|
||
|
Date: Mon, 22 Nov 2021 15:22:32 +0100
|
||
|
Subject: [PATCH 3/3] [policies] Deal 200 return code as success
|
||
|
|
||
|
Return code 200 of POST method request must be dealt as success.
|
||
|
|
||
|
Newly required due to the SFTP API change using POST.
|
||
|
|
||
|
Related to: #2764
|
||
|
|
||
|
Signed-off-by: Pavel Moravec <pmoravec@redhat.com>
|
||
|
---
|
||
|
sos/policies/distros/__init__.py | 2 +-
|
||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||
|
|
||
|
diff --git a/sos/policies/distros/__init__.py b/sos/policies/distros/__init__.py
|
||
|
index 0906fa779..6f257fdce 100644
|
||
|
--- a/sos/policies/distros/__init__.py
|
||
|
+++ b/sos/policies/distros/__init__.py
|
||
|
@@ -488,7 +488,7 @@ class LinuxPolicy(Policy):
|
||
|
r = self._upload_https_no_stream(arc)
|
||
|
else:
|
||
|
r = self._upload_https_streaming(arc)
|
||
|
- if r.status_code != 201:
|
||
|
+ if r.status_code != 200 and r.status_code != 201:
|
||
|
if r.status_code == 401:
|
||
|
raise Exception(
|
||
|
"Authentication failed: invalid user credentials"
|
||
|
From 8da1b14246226792c160dd04e5c7c75dd4e8d44b Mon Sep 17 00:00:00 2001
|
||
|
From: Pavel Moravec <pmoravec@redhat.com>
|
||
|
Date: Mon, 22 Nov 2021 10:44:09 +0100
|
||
|
Subject: [PATCH] [collect] fix moved get_upload_url under Policy class
|
||
|
|
||
|
SoSCollector does not further declare get_upload_url method
|
||
|
as that was moved under Policy class(es).
|
||
|
|
||
|
Resolves: #2766
|
||
|
|
||
|
Signed-off-by: Pavel Moravec <pmoravec@redhat.com>
|
||
|
---
|
||
|
sos/collector/__init__.py | 2 +-
|
||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||
|
|
||
|
diff --git a/sos/collector/__init__.py b/sos/collector/__init__.py
|
||
|
index 50183e873..42a7731d6 100644
|
||
|
--- a/sos/collector/__init__.py
|
||
|
+++ b/sos/collector/__init__.py
|
||
|
@@ -1219,7 +1219,7 @@ this utility or remote systems that it c
|
||
|
msg = 'No sosreports were collected, nothing to archive...'
|
||
|
self.exit(msg, 1)
|
||
|
|
||
|
- if self.opts.upload and self.get_upload_url():
|
||
|
+ if self.opts.upload and self.policy.get_upload_url():
|
||
|
try:
|
||
|
self.policy.upload_archive(arc_name)
|
||
|
self.ui_log.info("Uploaded archive successfully")
|
||
|
|
||
|
From abb2fc65bd14760021c61699ad3113cab3bd4c64 Mon Sep 17 00:00:00 2001
|
||
|
From: Pavel Moravec <pmoravec@redhat.com>
|
||
|
Date: Tue, 30 Nov 2021 11:37:02 +0100
|
||
|
Subject: [PATCH 1/2] [redhat] Fix broken URI to upload to customer portal
|
||
|
|
||
|
Revert back the unwanted change in URI of uploading tarball to the
|
||
|
Red Hat Customer portal.
|
||
|
|
||
|
Related: #2772
|
||
|
|
||
|
Signed-off-by: Pavel Moravec <pmoravec@redhat.com>
|
||
|
---
|
||
|
sos/policies/distros/redhat.py | 2 +-
|
||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||
|
|
||
|
diff --git a/sos/policies/distros/redhat.py b/sos/policies/distros/redhat.py
|
||
|
index e4e2b883..eb442407 100644
|
||
|
--- a/sos/policies/distros/redhat.py
|
||
|
+++ b/sos/policies/distros/redhat.py
|
||
|
@@ -250,7 +250,7 @@ support representative.
|
||
|
elif self.commons['cmdlineopts'].upload_protocol == 'sftp':
|
||
|
return RH_SFTP_HOST
|
||
|
else:
|
||
|
- rh_case_api = "/hydra/rest/cases/%s/attachments"
|
||
|
+ rh_case_api = "/support/v1/cases/%s/attachments"
|
||
|
return RH_API_HOST + rh_case_api % self.case_id
|
||
|
|
||
|
def _get_upload_headers(self):
|
||
|
--
|
||
|
2.31.1
|
||
|
|
||
|
|
||
|
From ea4f9e88a412c80a4791396e1bb78ac1e24ece14 Mon Sep 17 00:00:00 2001
|
||
|
From: Pavel Moravec <pmoravec@redhat.com>
|
||
|
Date: Tue, 30 Nov 2021 13:00:26 +0100
|
||
|
Subject: [PATCH 2/2] [policy] Add error message when FTP upload write failure
|
||
|
|
||
|
When (S)FTP upload fails to write the destination file,
|
||
|
our "expect" code should detect it sooner than after timeout happens
|
||
|
and write appropriate error message.
|
||
|
|
||
|
Resolves: #2772
|
||
|
|
||
|
Signed-off-by: Pavel Moravec <pmoravec@redhat.com>
|
||
|
---
|
||
|
sos/policies/distros/__init__.py | 5 ++++-
|
||
|
1 file changed, 4 insertions(+), 1 deletion(-)
|
||
|
|
||
|
diff --git a/sos/policies/distros/__init__.py b/sos/policies/distros/__init__.py
|
||
|
index 6f257fdc..7bdc81b8 100644
|
||
|
--- a/sos/policies/distros/__init__.py
|
||
|
+++ b/sos/policies/distros/__init__.py
|
||
|
@@ -473,7 +473,8 @@ class LinuxPolicy(Policy):
|
||
|
put_expects = [
|
||
|
u'100%',
|
||
|
pexpect.TIMEOUT,
|
||
|
- pexpect.EOF
|
||
|
+ pexpect.EOF,
|
||
|
+ u'No such file or directory'
|
||
|
]
|
||
|
|
||
|
put_success = ret.expect(put_expects, timeout=180)
|
||
|
@@ -485,6 +486,8 @@ class LinuxPolicy(Policy):
|
||
|
raise Exception("Timeout expired while uploading")
|
||
|
elif put_success == 2:
|
||
|
raise Exception("Unknown error during upload: %s" % ret.before)
|
||
|
+ elif put_success == 3:
|
||
|
+ raise Exception("Unable to write archive to destination")
|
||
|
else:
|
||
|
raise Exception("Unexpected response from server: %s" % ret.before)
|
||
|
|
||
|
--
|
||
|
2.31.1
|
||
|
|