Ejemplo n.º 1
0
async def test_load_contact_file_async(myflow):
    cont = await load_contact_file_async(myflow.suite)
    assert cont[CFF.HOST] == myflow.host

    # compare the async interface to the sync interface
    cont2 = load_contact_file(myflow.suite)
    assert cont == cont2
Ejemplo n.º 2
0
def get_location(suite: str, owner: str, host: str):
    """Extract host and port from a suite's contact file.

    NB: if it fails to load the suite contact file, it will exit.

    Args:
        suite (str): suite name
        owner (str): owner of the suite
        host (str): host name
    Returns:
        Tuple[str, int, int]: tuple with the host name and port numbers.
    Raises:
        ClientError: if the suite is not running.
    """
    try:
        contact = load_contact_file(suite, owner, host)
    except SuiteServiceFileError:
        raise ClientError(f'Contact info not found for suite '
                          f'"{suite}", suite not running?')

    if not host:
        host = contact[ContactFileFields.HOST]
    host = get_fqdn_by_host(host)

    port = int(contact[ContactFileFields.PORT])
    pub_port = int(contact[ContactFileFields.PUBLISH_PORT])
    return host, port, pub_port
Ejemplo n.º 3
0
def main(parser, options, reg):
    """CLI for "cylc get-suite-contact"."""
    try:
        data = load_contact_file(reg)
    except SuiteServiceFileError:
        raise CylcError(f"{reg}: cannot get contact info, suite not running?")
    else:
        for key, value in sorted(data.items()):
            print("%s=%s" % (key, value))
Ejemplo n.º 4
0
def _check_contact_file(scheduler):
    try:
        contact_data = suite_files.load_contact_file(
            scheduler.suite)
        if contact_data != scheduler.contact_data:
            raise CylcError('contact file modified')
    except (AssertionError, IOError, ValueError, SuiteServiceFileError):
        raise CylcError(
            '%s: contact file corrupted/modified and may be left'
            % suite_files.get_contact_file(scheduler.suite)
        )
Ejemplo n.º 5
0
    def send_request(self, command, args=None, timeout=None):
        """Send a request, using ssh.

        Determines ssh_cmd, cylc_path and login_shell settings from the contact
        file.

        Converts message to JSON and sends this to stdin. Executes the Cylc
        command, then deserialises the output.

        Use ``__call__`` to call this method.

        Args:
            command (str): The name of the endpoint to call.
            args (dict): Arguments to pass to the endpoint function.
            timeout (float): Override the default timeout (seconds).
        Raises:
            ClientError: Coverall, on error from function call
        Returns:
            object: Deserialized output from function called.
        """
        # Set environment variable to determine the communication for use on
        # the scheduler
        os.environ["CLIENT_COMMS_METH"] = CommsMeth.SSH
        cmd = ["client"]
        if timeout:
            cmd += [f'comms_timeout={timeout}']
        cmd += [self.suite, command]
        contact = load_contact_file(self.suite)
        ssh_cmd = contact[ContactFileFields.SCHEDULER_SSH_COMMAND]
        login_shell = contact[ContactFileFields.SCHEDULER_USE_LOGIN_SHELL]
        cylc_path = contact[ContactFileFields.SCHEDULER_CYLC_PATH]
        cylc_path = None if cylc_path == 'None' else cylc_path
        if not args:
            args = {}
        message = json.dumps(args)
        proc = _remote_cylc_cmd(cmd,
                                host=self.host,
                                stdin_str=message,
                                ssh_cmd=ssh_cmd,
                                remote_cylc_path=cylc_path,
                                ssh_login_shell=login_shell,
                                capture_process=True)

        out, err = (f.decode() for f in proc.communicate())
        return_code = proc.wait()
        if return_code:
            raise ClientError(err, f"return-code={return_code}")
        return json.loads(out)
Ejemplo n.º 6
0
async def test_scan_cleans_stuck_contact_files(run, scheduler, flow, one_conf,
                                               run_dir, test_dir):
    """Ensure scan tidies up contact files from crashed flows."""
    # create a flow
    reg = flow(one_conf, name='-crashed-')
    schd = scheduler(reg)
    srv_dir = Path(run_dir, reg, SuiteFiles.Service.DIRNAME)
    tmp_dir = test_dir / 'srv'
    cont = srv_dir / SuiteFiles.Service.CONTACT

    # run the flow, copy the contact, stop the flow, copy back the contact
    async with run(schd):
        copytree(srv_dir, tmp_dir)
    rmtree(srv_dir)
    copytree(tmp_dir, srv_dir)
    rmtree(tmp_dir)

    # the old contact file check uses the CLI command that the flow was run
    # with to check that whether the flow is running. Because this is an
    # integration test the process is the pytest process and it is still
    # running so we need to change the command so that Cylc sees the flow as
    # having crashed
    contact_info = load_contact_file(reg)
    contact_info[ContactFileFields.PROCESS] += 'xyz'
    dump_contact_file(reg, contact_info)

    # make sure this flow shows for a regular filesystem-only scan
    opts = ScanOptions(states='running,paused', format='name')
    flows = []
    await main(opts, write=flows.append, scan_dir=test_dir)
    assert len(flows) == 1
    assert '-crashed-' in flows[0]

    # the contact file should still be there
    assert cont.exists()

    # make sure this flow shows for a regular filesystem-only scan
    opts = ScanOptions(states='running,paused', format='name', ping=True)
    flows = []
    await main(opts, write=flows.append, scan_dir=test_dir)
    assert len(flows) == 0

    # the contact file should have been removed by the scan
    assert not cont.exists()
Ejemplo n.º 7
0
def get_location(suite: str):
    """Extract host and port from a suite's contact file.

    NB: if it fails to load the suite contact file, it will exit.

    Args:
        suite (str): suite name
    Returns:
        Tuple[str, int, int]: tuple with the host name and port numbers.
    Raises:
        ClientError: if the suite is not running.
    """
    try:
        contact = load_contact_file(suite)
    except SuiteServiceFileError:
        raise SuiteStopped(suite)

    host = contact[ContactFileFields.HOST]
    host = get_fqdn_by_host(host)
    port = int(contact[ContactFileFields.PORT])
    pub_port = int(contact[ContactFileFields.PUBLISH_PORT])
    return host, port, pub_port
Ejemplo n.º 8
0
def get_scan_items_from_fs(
        owner_pattern=None, reg_pattern=None, active_only=True):
    """Scrape list of suites from the filesystem.

    Walk users' "~/cylc-run/" to get (host, port) from ".service/contact" for
    active, or all (active plus registered but dormant), suites.

    Yields:
        tuple - (reg, host, port, pub_port, api)

    """
    if owner_pattern is None:
        # Run directory of current user only
        run_dirs = [(glbl_cfg().get_host_item('run directory'), None)]
    else:
        # Run directory of all users matching "owner_pattern".
        # But skip those with /nologin or /false shells
        run_dirs = []
        skips = ('/false', '/nologin')
        for pwent in getpwall():
            if any(pwent.pw_shell.endswith(s) for s in skips):
                continue
            if owner_pattern.match(pwent.pw_name):
                run_dirs.append((
                    glbl_cfg().get_host_item(
                        'run directory',
                        owner=pwent.pw_name,
                        owner_home=pwent.pw_dir),
                    pwent.pw_name))
    if cylc.flow.flags.debug:
        sys.stderr.write('Listing suites:%s%s\n' % (
            DEBUG_DELIM, DEBUG_DELIM.join(item[1] for item in run_dirs if
                                          item[1] is not None)))
    for run_d, owner in run_dirs:
        for dirpath, dnames, _ in os.walk(run_d, followlinks=True):
            # Always descend for top directory, but
            # don't descend further if it has a .service/ or log/ dir
            if dirpath != run_d and (
                    SuiteFiles.Service.DIRNAME
                    in dnames or 'log' in dnames):
                dnames[:] = []

            # Filter suites by name
            reg = os.path.relpath(dirpath, run_d)
            if reg_pattern and not reg_pattern.match(reg):
                continue

            # Choose only suites with .service and matching filter
            if active_only:
                try:
                    contact_data = load_contact_file(
                        reg, owner)
                except (SuiteServiceFileError, IOError, TypeError, ValueError):
                    continue
                yield (
                    reg,
                    contact_data[ContactFileFields.HOST],
                    contact_data[ContactFileFields.PORT],
                    contact_data[ContactFileFields.PUBLISH_PORT],
                    contact_data[ContactFileFields.API]
                )
            else:
                try:
                    source_dir = get_suite_source_dir(reg)
                    title = get_suite_title(reg)
                except (SuiteServiceFileError, IOError):
                    continue
                yield (
                    reg,
                    source_dir,
                    title
                )
Ejemplo n.º 9
0
def test_load_contact_file(myflow):
    cont = load_contact_file(myflow.suite)
    assert cont[CFF.HOST] == myflow.host