Skip to content

Commit

Permalink
[collect] Node and Inherit config files
Browse files Browse the repository at this point in the history
Signed-off-by: Trevor Benson <[email protected]>
  • Loading branch information
TrevorBenson committed Dec 2, 2024
1 parent 0c70fa9 commit 4fdbb38
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 2 deletions.
10 changes: 10 additions & 0 deletions sos/collector/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ class SoSCollector(SoSComponent):
'encrypt_pass': '',
'group': None,
'image': '',
'inherit_config_file': False,
'force_pull_image': True,
'skip_cleaning_files': [],
'jobs': 4,
Expand All @@ -102,6 +103,7 @@ class SoSCollector(SoSComponent):
'map_file': '/etc/sos/cleaner/default_mapping',
'primary': '',
'namespaces': None,
'node_config_file': None,
'nodes': [],
'no_env_vars': False,
'no_local': False,
Expand Down Expand Up @@ -463,6 +465,14 @@ def add_parser_options(cls, parser):
choices=['auto', 'https', 'ftp', 'sftp',
's3'],
help="Manually specify the upload protocol")
collect_grp.add_argument('--inherit-config-file', default=False,
action='store_true',
help='Use the config file from the collector '
'for all nodes to use with sos report')
collect_grp.add_argument('--node-config-file', type=str,
default=None,
help='Path to an existing config file on the '
'nodes to use with sos report')

# Group the cleaner options together
cleaner_grp = parser.add_argument_group(
Expand Down
17 changes: 17 additions & 0 deletions sos/collector/sosnode.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ def __init__(self, address, commons, password=None, local_sudo=None,
self.hostlen = commons['hostlen']
self.need_sudo = commons['need_sudo']
self.sos_options = commons['sos_options']
self.node_config_file = self.opts.node_config_file
self.inherit_config_file = self.opts.inherit_config_file
self.local = False
self.host = None
self.cluster = None
Expand Down Expand Up @@ -762,6 +764,21 @@ def execute_sos_command(self):
try:
path = False
checksum = False
config_file_arg = ''
if self.opts.node_config_file:
config_file_arg = f'--config-file={self.opts.node_config_file}'
elif self.opts.inherit_config_file:
if not self.local:
remote_config = f"/tmp/{self.tmpdir.split('/')[-1]}.conf"
self._transport.copy_file_to_remote(
self.opts.config_file,
remote_config)
config_file_arg = f'--config-file={remote_config}'
else:
config_file_arg = (
f'--config-file={self.opts.config_file}')
if config_file_arg:
self.sos_cmd = f"{self.sos_cmd} {config_file_arg}"
res = self.run_command(self.sos_cmd,
timeout=self.opts.timeout,
use_shell=True,
Expand Down
31 changes: 31 additions & 0 deletions sos/collector/transports/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,37 @@ def _get_hostname(self):
self.log_info(f"Hostname set to {self._hostname}")
return self._hostname

def copy_file_to_remote(self, fname, dest):
"""Copy a local file, fname, to dest on the remote node
:param fname: The name of the file to copy
:type fname: ``str``
:param dest: Where to save the file to remotely
:type dest: ``str``
:returns: True if file was successfully copied to remote, or False
:rtype: ``bool``
"""
attempts = 0
try:
while attempts < 3:
attempts += 1
ret = self._copy_file_to_remote(fname, dest)
if ret:
return True
self.log_info(f"File copy attempt {attempts} failed")
self.log_info("File copy failed after 3 attempts")
return False
except Exception as err:
self.log_error("Exception encountered during config copy attempt "
f"{attempts} for {fname}: {err}")
raise err

def _copy_file_to_remote(self, fname, dest):
raise NotImplementedError(
f"Transport {self.name} does not support file copying")

def retrieve_file(self, fname, dest):
"""Copy a remote file, fname, to dest on the local node
Expand Down
6 changes: 6 additions & 0 deletions sos/collector/transports/control_persist.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,12 @@ def remote_exec(self):
f"{self.opts.ssh_user}@{self.address}")
return self.ssh_cmd

def _copy_file_to_remote(self, fname, dest):
cmd = (f"/usr/bin/scp -oControlPath={self.control_path} "
f"{fname} {self.opts.ssh_user}@{self.address}:{dest}")
res = sos_get_command_output(cmd, timeout=10)
return res['status'] == 0

def _retrieve_file(self, fname, dest):
cmd = (f"/usr/bin/scp -oControlPath={self.control_path} "
f"{self.opts.ssh_user}@{self.address}:{fname} {dest}")
Expand Down
7 changes: 7 additions & 0 deletions sos/collector/transports/juju.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,13 @@ def remote_exec(self):
option = f"{model_option} {target_option}"
return f"juju ssh {option}"

def _copy_file_to_remote(self, fname, dest):
model, unit = self.address.split(":")
model_option = f"-m {model}" if model else ""
cmd = f"juju scp {model_option} -- {fname} {unit}:{dest}"
res = sos_get_command_output(cmd, timeout=15)
return res["status"] == 0

def _retrieve_file(self, fname, dest):
self._chmod(fname) # juju scp needs the archive to be world-readable
model, unit = self.address.split(":")
Expand Down
3 changes: 3 additions & 0 deletions sos/collector/transports/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ def _retrieve_file(self, fname, dest):
def _format_cmd_for_exec(self, cmd):
return cmd

def _copy_file_to_remote(self, fname, dest):
return True

def _read_file(self, fname):
if os.path.exists(fname):
with open(fname, 'r', encoding='utf-8') as rfile:
Expand Down
7 changes: 7 additions & 0 deletions sos/collector/transports/oc.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,13 @@ def remote_exec(self):
return (f"oc -n {self.project} exec --request-timeout=0 "
f"{self.pod_name} -- /bin/bash -c")

def _copy_file_to_remote(self, fname, dest):
result = self.run_oc("cp --retries", stderr=True)
flags = '' if "unknown flag" in result["output"] else '--retries=5'
cmd = self.run_oc(f"cp {flags} {fname} {self.pod_name}:{dest}",
timeout=15)
return cmd['status'] == 0

def _retrieve_file(self, fname, dest):
# check if --retries flag is available for given version of oc
result = self.run_oc("cp --retries", stderr=True)
Expand Down
28 changes: 26 additions & 2 deletions sos/collector/transports/saltstack.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ def run_command(self, cmd, timeout=180, need_root=False, env=None,
ret['output'] = self._convert_output_json(ret['output'])
return ret

def _salt_copy_file(self, node, fname, dest):
"""
Execute cp.get_file on the remote host using SaltStack Master
"""
cmd = f"salt-cp {node} {fname} {dest}"
res = sos_get_command_output(cmd, timeout=15)
return res['status'] == 0

def _salt_retrieve_file(self, node, fname, dest):
"""
Execute cp.push on the remote host using SaltStack Master
Expand Down Expand Up @@ -119,12 +127,28 @@ def remote_exec(self):
salt_args = "--out json --static --no-color"
return f"salt {salt_args} {self.address} cmd.shell "

def _copy_file_to_remote(self, fname, dest):
"""Copy a file to the remote host using SaltStack Master
Parameters
fname The path to the file on the master
dest The path to the destination directory on the remote host
Returns
True if the file was copied, else False
"""
return (
self._salt_copy_file(self.address, fname, dest)
if self.connected
else False
)

def _retrieve_file(self, fname, dest):
"""Retrieve a file from the remote host using saltstack
Parameters
fname The path to the file on the remote host
dest The path to the destination directory on the master
fname The path to the file on the remote host
dest The path to the destination directory on the master
Returns
True if the file was retrieved, else False
Expand Down

0 comments on commit 4fdbb38

Please sign in to comment.