Exemplo n.º 1
0
def main(is_restart=False):
    """CLI main."""
    options, args = parse_commandline(is_restart)
    if not args:
        # Auto-registration: "cylc run" (no args) in source dir.
        try:
            reg = SuiteSrvFilesManager().register()
        except SuiteServiceFileError as exc:
            sys.exit(exc)
        # Replace this process with "cylc run REG ..." for 'ps -f'.
        os.execv(sys.argv[0], [sys.argv[0]] + [reg] + sys.argv[1:])

    # Check suite is not already running before start of host selection.
    try:
        SuiteSrvFilesManager().detect_old_contact_file(args[0])
    except SuiteServiceFileError as exc:
        sys.exit(exc)

    # Create auth files if needed. On a shared FS if the suite host changes
    # this may (will?) renew the ssl.cert to reflect the change in host name.
    SuiteSrvFilesManager().create_auth_files(args[0])

    # Check whether a run host is explicitly specified, else select one.
    if not options.host:
        try:
            host = HostAppointer().appoint_host()
        except EmptyHostList as exc:
            if cylc.flags.debug:
                raise
            else:
                sys.exit(str(exc))
        if is_remote_host(host):
            if is_restart:
                base_cmd = ["restart"] + sys.argv[1:]
            else:
                base_cmd = ["run"] + sys.argv[1:]
            # Prevent recursive host selection
            base_cmd.append("--host=localhost")
            return remote_cylc_cmd(base_cmd, host=host)
    if remrun(set_rel_local=True):  # State localhost as above.
        sys.exit()

    try:
        SuiteSrvFilesManager().get_suite_source_dir(args[0], options.owner)
    except SuiteServiceFileError:
        # Source path is assumed to be the run directory
        SuiteSrvFilesManager().register(
            args[0],
            glbl_cfg().get_derived_host_item(args[0], 'suite run directory'))

    try:
        scheduler = Scheduler(is_restart, options, args)
    except SuiteServiceFileError as exc:
        sys.exit(exc)
    scheduler.start()
Exemplo n.º 2
0
def main(is_restart=False):
    """CLI main."""
    options, args = parse_commandline(is_restart)
    if not args:
        # Auto-registration: "cylc run" (no args) in source dir.
        try:
            reg = SuiteSrvFilesManager().register()
        except SuiteServiceFileError as exc:
            sys.exit(exc)
        # Replace this process with "cylc run REG ..." for 'ps -f'.
        os.execv(sys.argv[0], [sys.argv[0]] + [reg] + sys.argv[1:])

    # Check suite is not already running before start of host selection.
    try:
        SuiteSrvFilesManager().detect_old_contact_file(args[0])
    except SuiteServiceFileError as exc:
        sys.exit(exc)

    # Create auth files if needed.
    SuiteSrvFilesManager().create_auth_files(args[0])

    # Check whether a run host is explicitly specified, else select one.
    if not options.host:
        try:
            host = HostAppointer().appoint_host()
        except EmptyHostList as exc:
            if cylc.flags.debug:
                raise
            else:
                sys.exit(str(exc))
        if is_remote_host(host):
            if is_restart:
                base_cmd = ["restart"] + sys.argv[1:]
            else:
                base_cmd = ["run"] + sys.argv[1:]
            # Prevent recursive host selection
            base_cmd.append("--host=localhost")
            return remote_cylc_cmd(base_cmd, host=host)
    if remrun(set_rel_local=True):  # State localhost as above.
        sys.exit()

    try:
        SuiteSrvFilesManager().get_suite_source_dir(args[0], options.owner)
    except SuiteServiceFileError:
        # Source path is assumed to be the run directory
        SuiteSrvFilesManager().register(
            args[0],
            glbl_cfg().get_derived_host_item(args[0], 'suite run directory'))

    try:
        scheduler = Scheduler(is_restart, options, args)
    except SuiteServiceFileError as exc:
        sys.exit(exc)
    scheduler.start()
Exemplo n.º 3
0
    def _call_server_via_comms2(self, function, payload, **kwargs):
        """Call server via "cylc client --use-ssh".

        Call "cylc client --use-ssh" using `subprocess.Popen`. Payload and
        arguments of the API method are serialized as JSON and are written to a
        temporary file, which is then used as the STDIN of the "cylc client"
        command. The external call here should be even safer than a direct
        HTTP(S) call, as it can be blocked by SSH before it even gets a chance
        to make the subsequent HTTP(S) call.

        Arguments:
            function (str): name of API method, argument 1 of "cylc client".
            payload (str): extra data or information for the API method.
            **kwargs (dict): arguments for the API method.
        """
        import json
        from cylc.remote import remote_cylc_cmd
        command = ["client", function, self.suite]
        if payload:
            kwargs["payload"] = payload
        if kwargs:
            from tempfile import TemporaryFile
            stdin = TemporaryFile()
            json.dump(kwargs, stdin)
            stdin.seek(0)
        else:
            # With stdin=None, `remote_cylc_cmd` will:
            # * Set stdin to open(os.devnull)
            # * Add `-n` to the SSH command
            stdin = None
        proc = remote_cylc_cmd(
            command,
            self.owner,
            self.host,
            capture=True,
            ssh_login_shell=(self.comms1.get(
                self.srv_files_mgr.KEY_SSH_USE_LOGIN_SHELL)
                             in ['True', 'true']),
            ssh_cylc=(
                r'%s/bin/cylc' %
                self.comms1.get(self.srv_files_mgr.KEY_DIR_ON_SUITE_HOST)),
            stdin=stdin,
        )
        out = proc.communicate()[0]
        return_code = proc.wait()
        if return_code:
            from pipes import quote
            command_str = " ".join(quote(item) for item in command)
            raise ClientError(command_str, "return-code=%d" % return_code)
        return json.loads(out)
Exemplo n.º 4
0
    def _get_host_metrics(self):
        """Run "cylc get-host-metrics" commands on hosts.

        Return (dict): {host: host-metrics-dict, ...}
        """
        host_stats = {}
        # Run "cylc get-host-metrics" commands on hosts
        host_proc_map = {}
        cmd = [self.CMD_BASE] + sorted(self._get_host_metrics_opts())
        # Start up commands on hosts
        for host in self.hosts:
            if is_remote_host(host):
                host_proc_map[host] = remote_cylc_cmd(cmd,
                                                      stdin=None,
                                                      host=host,
                                                      capture_process=True)
            elif 'localhost' in host_proc_map:
                continue  # Don't duplicate localhost
            else:
                # 1st instance of localhost
                host_proc_map['localhost'] = run_cmd(['cylc'] + cmd,
                                                     capture_process=True)
        # Collect results from commands
        while host_proc_map:
            for host, proc in list(host_proc_map.copy().items()):
                if proc.poll() is None:
                    continue
                del host_proc_map[host]
                out, err = (f.decode() for f in proc.communicate())
                if proc.wait():
                    # Command failed in verbose/debug mode
                    LOG.warning(
                        "can't get host metric from '%s'" +
                        "%s  # returncode=%d, err=%s\n", host, ' '.join(
                            (quote(item) for item in cmd)), proc.returncode,
                        err)
                else:
                    # Command OK
                    # Users may have profile scripts that write to STDOUT.
                    # Drop all output lines until the the first character of a
                    # line is '{'. Hopefully this is enough to find us the
                    # first line that denotes the beginning of the expected
                    # JSON data structure.
                    out = ''.join(
                        dropwhile(lambda s: not s.startswith('{'),
                                  out.splitlines(True)))
                    host_stats[host] = json.loads(out)
            sleep(0.01)
        return host_stats
Exemplo n.º 5
0
    def _call_server_via_comms2(self, function, payload, **kwargs):
        """Call server via "cylc client --use-ssh".

        Call "cylc client --use-ssh" using `subprocess.Popen`. Payload and
        arguments of the API method are serialized as JSON and are written to a
        temporary file, which is then used as the STDIN of the "cylc client"
        command. The external call here should be even safer than a direct
        HTTP(S) call, as it can be blocked by SSH before it even gets a chance
        to make the subsequent HTTP(S) call.

        Arguments:
            function (str): name of API method, argument 1 of "cylc client".
            payload (str): extra data or information for the API method.
            **kwargs (dict): arguments for the API method.
        """
        import json
        from cylc.remote import remote_cylc_cmd
        command = ["client", function, self.suite]
        if payload:
            kwargs["payload"] = payload
        if kwargs:
            from tempfile import TemporaryFile
            stdin = TemporaryFile()
            json.dump(kwargs, stdin)
            stdin.seek(0)
        else:
            # With stdin=None, `remote_cylc_cmd` will:
            # * Set stdin to open(os.devnull)
            # * Add `-n` to the SSH command
            stdin = None
        proc = remote_cylc_cmd(
            command, self.owner, self.host, capture=True,
            ssh_login_shell=(self.comms1.get(
                self.srv_files_mgr.KEY_SSH_USE_LOGIN_SHELL
            ) in ['True', 'true']),
            ssh_cylc=(r'%s/bin/cylc' % self.comms1.get(
                self.srv_files_mgr.KEY_DIR_ON_SUITE_HOST)
            ),
            stdin=stdin,
        )
        out = proc.communicate()[0]
        return_code = proc.wait()
        if return_code:
            from pipes import quote
            command_str = " ".join(quote(item) for item in command)
            raise ClientError(command_str, "return-code=%d" % return_code)
        return json.loads(out)
Exemplo n.º 6
0
    def _get_host_metrics(self):
        """Run "cylc get-host-metrics" commands on hosts.

        Return (dict): {host: host-metrics-dict, ...}
        """
        host_stats = {}
        # Run "cylc get-host-metrics" commands on hosts
        host_proc_map = {}
        cmd = [self.CMD_BASE] + sorted(self._get_host_metrics_opts())
        # Start up commands on hosts
        for host in self.hosts:
            if is_remote_host(host):
                host_proc_map[host] = remote_cylc_cmd(
                    cmd, stdin=None, host=host, capture_process=True)
            elif 'localhost' in host_proc_map:
                continue  # Don't duplicate localhost
            else:
                # 1st instance of localhost
                host_proc_map['localhost'] = run_cmd(
                    ['cylc'] + cmd, capture_process=True)
        # Collect results from commands
        while host_proc_map:
            for host, proc in host_proc_map.copy().items():
                if proc.poll() is None:
                    continue
                del host_proc_map[host]
                out, err = proc.communicate()
                if proc.wait():
                    # Command failed in verbose/debug mode
                    LOG.warning(
                        "can't get host metric from '%s'" +
                        "%s  # returncode=%d, err=%s\n",
                        host, ' '.join((quote(item) for item in cmd)),
                        proc.returncode, err)
                else:
                    # Command OK
                    host_stats[host] = json.loads(out)
            sleep(0.01)
        return host_stats