From 5298080d7360202c72b0af2e24994e4bfad72322 Mon Sep 17 00:00:00 2001 From: Jake Hunsaker 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 --- 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 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 --- 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 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 --- 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 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/, which is confusing. Let remove the path from it also in case_id not supplied. Related to: #2764 Signed-off-by: Pavel Moravec --- 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 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 --- 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 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 --- 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 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 --- 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 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 --- 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 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 --- 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