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
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
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))
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) )
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)
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()
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
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 )
def test_load_contact_file(myflow): cont = load_contact_file(myflow.suite) assert cont[CFF.HOST] == myflow.host