コード例 #1
0
def getValidNfsServerAddress(ctx):
    """ Get valid NFS server address serving the OCP cluster net """

    # Get all IP addresses for NFS server
    allIps = getAllNfsServerIpAddresses(ctx)

    # Need an OC login to get worker node address
    ocp = Ocp(ctx, login="******", verify=True)
    worker = ocp.getWorkerNodeList()[0]

    # Run Python script tools/modules/nfs-ping-test on helper node (in-line)
    cmdShell = CmdShell()
    host = ctx.cf.ocp.helper.host.name
    user = ctx.cr.ocp.helper.user
    repo = ctx.cf.build.repo.root
    toolCmd = f'python3 - <{repo}/tools/modules/nfs-ping-test {worker} {allIps}'

    cmdSsh = CmdSsh(ctx, host, user, reuseCon=False)
    runCmd, secr = cmdSsh.getSshCmdAndSecrets(withLogin=True)

    ipAddr = cmdShell.run(f'{runCmd} {toolCmd}', secrets=secr).out
    logging.debug(
        f"Running shell cmd: '{runCmd} {toolCmd}' returns '{ipAddr}'")
    if ipAddr == 'None':
        message = "Could not identify valid IP address for the NFS server"
        message += f"on worker node {worker}.\n"
        fail(message)

    del ocp
    return ipAddr
コード例 #2
0
class AuthorizedKeys():
    """ Representation of an authorized_keys file of a specific user at a specific host """
    def __init__(self, ctx, hname, user):

        self._userFull = f'{user.name}@{hname}'
        self._authKeys = None

        # Prepare command execution

        self._cmdShell = CmdShell()
        self._cmdSsh = CmdSsh(ctx, hname, user)
        self._rsyncSsh, self._rsyncSshSecrets = self._cmdSsh.getSshCmdAndSecrets(
            withLogin=False)

        # Get the path to the remote authorized_keys file

        homeDir = getHomeDir(ctx, hname, user)

        if not homeDir:
            fail(
                f"Could not determine the home directory of '{self._userFull}'"
            )

        self._akPath = f'{homeDir}/.ssh/authorized_keys'

        # Read the authorized_keys file

        info = _PublicKeyInformation(hname, user, self._akPath)

        self._read(info)  # Sets self._authKeys

        logging.debug(
            f'self._authKeys >>>\n{self._authKeys}\n<<< self._authKeys')

    def __str__(self):
        return str(self._authKeys)

    def _read(self, info):
        """ Read the contents of the authorized_keys file """

        res = self._cmdSsh.run(f'cat {self._akPath}')

        if res.rc != 0:
            fail(
                f"Could not get the authorized keys '{self._akPath}' file of '{self._userFull}'"
            )

        self._authKeys = _PublicKeys(res.out, info, keepAll=True)

    def write(self):
        """ Write the contents of the authorized_keys file """

        # Write the contents to a temporary local file and transfer the file to
        # the remote user's .ssh directory using rsync
        # Keep a backup of the original authorized_keys file on the remote side

        with tempfile.NamedTemporaryFile(mode='w') as akFh:
            print(self._authKeys, file=akFh, flush=True)

            source = akFh.name
            target = self._akPath

            backupSuffix = '.bak'

            rsyncCmd = f'rsync -av -e "{self._rsyncSsh}"'
            rsyncCmd += f' --backup --suffix "{backupSuffix}"'
            rsyncCmd += f' "{source}" "{self._userFull}:{target}"'

            res = self._cmdShell.run(rsyncCmd, self._rsyncSshSecrets)

            if res.rc != 0:
                fail(f"Could not write the authorized keys file '{target}'"
                     f" of '{self._userFull}\n({res.err})")

    def add(self, keys):
        """ Add keys in a key list to the internal authorized_keys list """
        return self._authKeys.addKeys(keys)

    def remove(self, keys):
        """ Remove keys in a key list from internal authorized_keys list """
        return self._authKeys.removeKeys(keys)

    def numKeys(self):
        """ Return the number of keys in the internal authorized_keys key list """
        return self._authKeys.numKeys()
コード例 #3
0
class RemoteCopy():
    """ Perform selective remote copy with correct symlink preservation """

    def __init__(self, ctx, host, user, filterFilePath='/dev/null'):
        """Perform selective remote copy with correct symlink preservation

        Parameters:

        host:           host of source directory; also ssh and rsync host
        user:           ssh and rsync user
        filterFilePath: optional; path to rsync filter file

        """

        self._host = host
        self._user = user
        self._filterFilePath = filterFilePath

        # Initialize ssh connection

        self._cmdSsh = CmdSsh(ctx, host, user)
        self._rsyncSsh, self._rsyncSshSecrets = self._cmdSsh.getSshCmdAndSecrets(withLogin=False)

    def _runRsync(self, source, filterFilePath, verbose, dryRun):
        logging.debug(f'source: >>>{source}<<<')
        cmdShell = CmdShell()
        cmd = 'rsync -a --relative'
        cmd += f' -e "{self._rsyncSsh}"'
        if verbose > 0:
            cmd += ' -'+'v'*verbose
        cmd += f' -f "merge {filterFilePath}"'
        if dryRun:
            cmd += ' -n'
        if not isinstance(source, list):
            cmd += f' {self._user.name}@{self._host}:{source} ./'
            cmdShell.run(cmd, self._rsyncSshSecrets)
        else:
            with tempfile.NamedTemporaryFile(mode='w') as tfh:
                tfh.write("\n".join(str(fn) for fn in source))
                tfh.flush()
                logging.debug(f"Contents of file '{tfh.name}':")
                logging.debug('>>>')
                # pylint: disable=unspecified-encoding
                with open(tfh.name) as rfh:
                    logging.debug(rfh.read())
                logging.debug('<<<')
                cmd += f' -r --files-from={tfh.name}'
                cmd += f' {self._user.name}@{self._host}:/ ./'
                cmdShell.run(cmd, self._rsyncSshSecrets)

    def _getRealPathAndSymlinks(self, path, targets):
        logging.debug(f"path '{path}', targets '{targets}'")
        curPath = ''
        for component in list(filter(lambda x: x != '', path.split('/'))):
            curDir = curPath
            curPath = curDir + '/' + component
            logging.debug(f"Current path '{curPath}'")
            if curPath not in targets.keys():
                logging.debug(f"Visiting new path '{curPath}'")
                target = self._cmdSsh.run(f'readlink {curPath}').out
                if not target:
                    targets[curPath] = None
                else:
                    logging.debug(f"found symlink '{curPath}', target '{target}'")
                    if not target.startswith('/'):
                        relTarget = target
                        target = os.path.normpath(curDir + '/' + target)
                        logging.debug(f"Converted relative target '{relTarget}'"
                                      f" to absolute target '{target}'")
                    (targets[curPath], targets) = self._getRealPathAndSymlinks(target, targets)
            if targets[curPath]:
                curPath = targets[curPath]
        logging.debug(f"returning path '{curPath}', targets >>>{targets}<<<")
        return (curPath, targets)

    def _symlinkConvertRelToAbs(self, symlink, linkTarget):
        if not linkTarget.startswith('/'):
            relTarget = linkTarget
            linkTarget = os.path.join(os.path.dirname(symlink), linkTarget)[1:]  # skip leading '.'
            logging.debug(f"Converted relative target '{relTarget}' "
                          f"to absolute target '{linkTarget}'")
        return linkTarget

    def copy(self, source, filterFilePath=None, verbose=1, dryRun=False):
        """ Perform remote copy

        Parameters:

        source        : root of source directorytree; must be an absolute path
        filterFilePath: optional: path to file containing rsync filters;
                        if not supplied filter file path supplied to constructor will be used
        verbose       : optional: set rsync verbose level;
                        choices: [0, 1, 2, 3], corresponding to rsync verbose levels
                        [<none>, '-v', '-vv', '-vvv']
        dryRun        : optional: if set to True, perform a trial rsync run with no changes made'

        """

        if not filterFilePath:
            filterFilePath = self._filterFilePath

        logging.info(f"Remote copy of '{source}' started.")

        logging.debug(f'source         >>>{source        }<<<')
        logging.debug(f'filterFilePath >>>{filterFilePath}<<<')
        logging.debug(f'verbose        >>>{verbose       }<<<')
        logging.debug(f'dryRun         >>>{dryRun        }<<<')

        cmdShell = CmdShell()

        symlinksVisited = []
        existingSymlinks = cmdShell.run('find ./ -type l').out
        if existingSymlinks:
            # Do not follow local existing symlinks
            symlinksVisited = existingSymlinks.strip().split('\n')
        logging.debug(f'symlinksVisited >>>{symlinksVisited}<<<')
        # rsync root of source directory supplied on command line
        (realPath, targets) = self._getRealPathAndSymlinks(source, {})
        self._runRsync(realPath, filterFilePath, verbose, dryRun)
        # If root of source directory tree is a symlink itself append it to list of visited links
        logging.debug(f"source  : '{source}'")
        logging.debug(f"realPath: '{realPath}'")
        if realPath != source:
            # cmdShell.run(f'ln -s {realPath} .{source}')
            symlinksVisited.append(f'.{source}')
        # Recursively detect all symlinks and rsync their targets
        finished = False
        while not finished:
            finished = True
            symlinksFound = cmdShell.run('find ./ -type l').out
            logging.debug(f'symlinksFound >>>{symlinksFound}<<<')
            logging.debug(f'symlinksVisited >>>{symlinksVisited}<<<')
            if symlinksFound:
                symlinksFound = symlinksFound.strip().split('\n')
                realPaths = []
                for symlink in symlinksFound:
                    if (symlink not in symlinksVisited
                            and symlink[1:] not in targets.keys()):  # skip leading '.'
                        logging.debug(f'symlink >>>{symlink}<<<')
                        linkTarget = os.readlink(symlink)
                        logging.debug(f'linkTarget >>>{linkTarget}<<<')
                        linkTarget = self._symlinkConvertRelToAbs(symlink, linkTarget)
                        (realPath, targets) = self._getRealPathAndSymlinks(linkTarget, targets)
                        if realPath not in realPaths:
                            realPaths.append(realPath)
                            logging.debug(f'realPaths: >>>{realPaths}<<<')
                        symlinksVisited.append(symlink)
                        finished = False
                if realPaths:
                    self._runRsync(realPaths, filterFilePath, verbose, dryRun)
        # Copy all symlinks that were not yet copied
        logging.debug('Final rsync call')
        self._runRsync([s for (s, t) in targets.items() if t],
                       filterFilePath, verbose, dryRun)
        logging.info(f"Remote copy of '{source}' finished.")