Exemplo n.º 1
0
def run(script_path=None, **kwargs):
    ctx = operation_ctx._get_current_object()
    script_path = get_script_to_run(script_path, ctx)
    if not script_path:
        return
    script_func = get_run_script_func(script_path, ctx)
    return process(script_func, script_path, ctx)
Exemplo n.º 2
0
def run(script_path, process=None, **kwargs):
    ctx = operation_ctx._get_current_object()
    if script_path is None:
        raise NonRecoverableError('Script path parameter not defined')
    process = create_process_config(process or {}, kwargs)
    script_path = download_resource(ctx.download_resource, script_path)
    os.chmod(script_path, 0755)
    script_func = get_run_script_func(script_path, process)
    return process_execution(script_func, script_path, ctx, process)
Exemplo n.º 3
0
def run(script_path, process=None, **kwargs):
    ctx = operation_ctx._get_current_object()
    if script_path is None:
        raise NonRecoverableError('Script path parameter not defined')
    process = create_process_config(process or {}, kwargs)
    script_path = download_resource(ctx.download_resource, script_path)
    os.chmod(script_path, 0755)
    script_func = get_run_script_func(script_path, process)
    return process_execution(script_func, script_path, ctx, process)
Exemplo n.º 4
0
def calljava(opclass, args, **kwargs):
    """ Should do whatever "create" is defined as in java code.  It is assumed
        that the java implementation main function accepts an Operation class
        name and a varargs list
    """
    proxy_server = HTTPCtxProxy(ctx._get_current_object())

    try:
        jarpath = os.path.dirname(os.path.abspath(__file__)) + "/plugin.jar"
        # below will fail on windows
        res = subprocess.call(
            ["java", "-jar", jarpath,
             str(proxy_server.port), opclass] + args)
        if res != 0:
            raise NonRecoverableError(
                "operation {} execution failed".format(opclass))
    finally:
        proxy_server.close()
Exemplo n.º 5
0
def callgo(func, args, **kwargs):
    """ Should do whatever "create" is defined as in go code.  It is assumed
        that the go implementation main function accepts a funcion name and
        a varargs list
    """
    install()
    proxy_server = HTTPCtxProxy(ctx._get_current_object())

    try:
        exepath = ctx.instance.runtime_properties['plugin_path']
        # below will fail on windows
        res = subprocess.call(
            [exepath, str(proxy_server.port), func,
             json.dumps(args)])
        if res != 0:
            raise NonRecoverableError("func {} execution faild".format(func))
    finally:
        proxy_server.close()
Exemplo n.º 6
0
def run_script(script_path,
               fabric_env=None,
               process=None,
               use_sudo=False,
               hide_output=None,
               **kwargs):

    if not process:
        process = {}
    process = _create_process_config(process, kwargs)
    ctx_server_port = process.get('ctx_server_port')

    proxy_client_path = proxy_client.__file__
    if proxy_client_path.endswith('.pyc'):
        proxy_client_path = proxy_client_path[:-1]
    local_ctx_sh_path = os.path.join(_get_bin_dir(), 'ctx-sh')
    local_ctx_py_path = os.path.join(
        os.path.dirname(cloudify.ctx_wrappers.__file__), 'ctx-py.py')
    local_script_path = get_script(ctx.download_resource, script_path)
    base_script_path = os.path.basename(local_script_path)
    remote_path_suffix = '{0}-{1}'.format(base_script_path,
                                          utils.id_generator(size=8))

    env = process.get('env', {})
    args = process.get('args')
    command_prefix = process.get('command_prefix')

    with fabric_api.settings(
            _hide_output(groups=hide_output),
            **_fabric_env(fabric_env, warn_only=False)):
        # Determine the basedir. In order of precedence:

        # * 'base_dir' process input
        # * ${CFY_EXEC_TEMP}/cloudify-ctx on the remote machine, if
        #   CFY_EXEC_TEMP is defined
        # * <python default tempdir>/cloudify-ctx

        base_dir = process.get('base_dir')

        if not base_dir:
            #  "CFY_EXEC_TEMPDIR_ENVVAR" doesn't exist in 3.3.1, so
            # to remain backward compatible...
            if hasattr(utils, 'CFY_EXEC_TEMPDIR_ENVVAR'):
                base_dir = fabric_api.run(
                    '( [[ -n "${0}" ]] && echo -n ${0} ) || '
                    'echo -n $(dirname $(mktemp -u))'.format(
                        utils.CFY_EXEC_TEMPDIR_ENVVAR))
            else:
                base_dir = fabric_api.run('echo -n $(dirname $(mktemp -u))')

        if not base_dir:
            raise NonRecoverableError('Could not conclude temporary directory')

        base_dir = posixpath.join(base_dir, DEFAULT_BASE_SUBDIR)

        ctx.logger.debug('base_dir set to: {0}'.format(base_dir))

        remote_ctx_dir = base_dir
        remote_ctx_path = '{0}/ctx'.format(remote_ctx_dir)
        remote_ctx_sh_path = '{0}/ctx-sh'.format(remote_ctx_dir)
        remote_ctx_py_path = '{0}/cloudify.py'.format(remote_ctx_dir)
        remote_scripts_dir = '{0}/scripts'.format(remote_ctx_dir)
        remote_work_dir = '{0}/work'.format(remote_ctx_dir)
        remote_env_script_path = '{0}/env-{1}'.format(remote_scripts_dir,
                                                      remote_path_suffix)
        remote_script_path = '{0}/{1}'.format(remote_scripts_dir,
                                              remote_path_suffix)

        cwd = process.get('cwd', remote_work_dir)

        command = remote_script_path
        if command_prefix:
            command = '{0} {1}'.format(command_prefix, command)
        if args:
            command = ' '.join([command] + args)

        # the remote host must have ctx and any related files before
        # running any fabric scripts
        if not fabric_files.exists(remote_ctx_path):
            # there may be race conditions with other operations that
            # may be running in parallel, so we pass -p to make sure
            # we get 0 exit code if the directory already exists
            fabric_api.run('mkdir -p {0}'.format(remote_scripts_dir))
            fabric_api.run('mkdir -p {0}'.format(remote_work_dir))
            # this file has to be present before using ctx
            fabric_api.put(local_ctx_sh_path, remote_ctx_sh_path)
            fabric_api.put(proxy_client_path, remote_ctx_path)
            fabric_api.put(local_ctx_py_path, remote_ctx_py_path)

        actual_ctx = ctx._get_current_object()

        actual_ctx.is_script_exception_defined = ScriptException is not None

        def abort_operation(message=None):
            if actual_ctx._return_value is not None:
                actual_ctx._return_value = ILLEGAL_CTX_OPERATION_ERROR
                raise actual_ctx._return_value
            if actual_ctx.is_script_exception_defined:
                actual_ctx._return_value = ScriptException(message)
            else:
                actual_ctx._return_value = UNSUPPORTED_SCRIPT_FEATURE_ERROR
                raise actual_ctx
            return actual_ctx._return_value

        def retry_operation(message=None, retry_after=None):
            if actual_ctx._return_value is not None:
                actual_ctx._return_value = ILLEGAL_CTX_OPERATION_ERROR
                raise actual_ctx._return_value
            actual_ctx.operation.retry(message=message,
                                       retry_after=retry_after)
            if actual_ctx.is_script_exception_defined:
                actual_ctx._return_value = ScriptException(message, retry=True)
            else:
                actual_ctx._return_value = UNSUPPORTED_SCRIPT_FEATURE_ERROR
                raise actual_ctx._return_value
            return actual_ctx._return_value

        actual_ctx.abort_operation = abort_operation
        actual_ctx.retry_operation = retry_operation

        def returns(_value):
            if actual_ctx._return_value is not None:
                actual_ctx._return_value = ILLEGAL_CTX_OPERATION_ERROR
                raise actual_ctx._return_value
            actual_ctx._return_value = _value
        actual_ctx.returns = returns

        actual_ctx._return_value = None
        original_download_resource = actual_ctx.download_resource

        def download_resource(resource_path, target_path=None):
            local_target_path = original_download_resource(resource_path)
            return fabric_put_in_remote_path(local_target_path, target_path)

        actual_ctx.download_resource = download_resource

        original_download_resource_and_render = \
            actual_ctx.download_resource_and_render

        def download_resource_and_render(resource_path,
                                         target_path=None,
                                         template_variables=None):
            local_target_path = original_download_resource_and_render(
                resource_path,
                template_variables=template_variables)

            return fabric_put_in_remote_path(local_target_path, target_path)

        actual_ctx.download_resource_and_render = download_resource_and_render

        def fabric_put_in_remote_path(local_target_path, target_path):
            if target_path:
                remote_target_path = target_path
            else:
                remote_target_path = '{0}/{1}'.format(
                    remote_work_dir,
                    os.path.basename(local_target_path))
            fabric_api.put(local_target_path, remote_target_path)
            return remote_target_path

        def handle_script_result(script_result):
            if (actual_ctx.is_script_exception_defined and
               isinstance(script_result, ScriptException)):
                if script_result.retry:
                    return script_result
                else:
                    raise NonRecoverableError(str(script_result))
            # this happens when more than 1 ctx operation is invoked or
            # the plugin runs an unsupported feature on older Cloudify
            elif isinstance(script_result, RuntimeError):
                raise NonRecoverableError(str(script_result))
            # determine if this code runs during exception handling
            current_exception = sys.exc_info()[1]
            if current_exception:
                raise
            else:
                return script_result

        env_script = StringIO()
        env['PATH'] = '{0}:$PATH'.format(remote_ctx_dir)
        env['PYTHONPATH'] = '{0}:$PYTHONPATH'.format(remote_ctx_dir)
        env_script.write('chmod +x {0}\n'.format(remote_script_path))
        env_script.write('chmod +x {0}\n'.format(remote_ctx_path))
        fabric_api.put(local_script_path, remote_script_path)
        proxy = proxy_server.HTTPCtxProxy(actual_ctx, port=ctx_server_port)
        try:
            with fabric_context.cd(cwd):
                local_port = proxy.port
                with tunnel.remote(local_port=local_port) as remote_port:
                    env[CTX_SOCKET_URL] = proxy.socket_url.replace(
                        str(local_port), str(remote_port))
                    env['LOCAL_{0}'.format(CTX_SOCKET_URL)] = proxy.socket_url
                    for key, value in env.iteritems():
                        env_script.write('export {0}={1}\n'.format(key, value))
                    fabric_api.put(env_script, remote_env_script_path)
                    # invoke sys.exc_clear() because handle_script_result
                    # invokes sys.exc_info()
                    sys.exc_clear()
                    try:
                        command = 'source {0} && {1}'.format(
                            remote_env_script_path, command)
                        run = fabric_api.sudo if use_sudo else fabric_api.run
                        run(command)
                    except FabricTaskError:
                        return handle_script_result(actual_ctx._return_value)
            return handle_script_result(actual_ctx._return_value)
        finally:
            proxy.close()
Exemplo n.º 7
0
def run_script(script_path, fabric_env=None, process=None, **kwargs):

    if not process:
        process = {}
    process = _create_process_config(process, kwargs)
    base_dir = process.get('base_dir', DEFAULT_BASE_DIR)
    ctx_server_port = process.get('ctx_server_port')

    proxy_client_path = proxy_client.__file__
    if proxy_client_path.endswith('.pyc'):
        proxy_client_path = proxy_client_path[:-1]
    local_ctx_sh_path = os.path.join(_get_bin_dir(),
                                     'ctx-sh')
    local_script_path = get_script(ctx.download_resource, script_path)
    base_script_path = os.path.basename(local_script_path)
    remote_ctx_dir = base_dir
    remote_ctx_path = '{0}/ctx'.format(remote_ctx_dir)
    remote_ctx_sh_path = '{0}/ctx-sh'.format(remote_ctx_dir)
    remote_scripts_dir = '{0}/scripts'.format(remote_ctx_dir)
    remote_work_dir = '{0}/work'.format(remote_ctx_dir)
    remote_path_suffix = '{0}-{1}'.format(base_script_path,
                                          utils.id_generator(size=8))
    remote_env_script_path = '{0}/env-{1}'.format(remote_scripts_dir,
                                                  remote_path_suffix)
    remote_script_path = '{0}/{1}'.format(remote_scripts_dir,
                                          remote_path_suffix)

    env = process.get('env', {})
    cwd = process.get('cwd', remote_work_dir)
    args = process.get('args')
    command_prefix = process.get('command_prefix')

    command = remote_script_path
    if command_prefix:
        command = '{0} {1}'.format(command_prefix, command)
    if args:
        command = ' '.join([command] + args)

    with fabric_api.settings(**_fabric_env(fabric_env, warn_only=False)):
        # the remote host must have ctx and any related files before
        # running any fabric scripts
        if not fabric_files.exists(remote_ctx_path):

            # there may be race conditions with other operations that
            # may be running in parallel, so we pass -p to make sure
            # we get 0 exit code if the directory already exists
            fabric_api.run('mkdir -p {0}'.format(remote_scripts_dir))
            fabric_api.run('mkdir -p {0}'.format(remote_work_dir))

            # this file has to be present before using ctx
            fabric_api.put(local_ctx_sh_path, remote_ctx_sh_path)

            fabric_api.put(proxy_client_path, remote_ctx_path)

        actual_ctx = ctx._get_current_object()

        actual_ctx.is_script_exception_defined = ScriptException is not None

        def abort_operation(message=None):
            if actual_ctx._return_value is not None:
                actual_ctx._return_value = ILLEGAL_CTX_OPERATION_ERROR
                raise actual_ctx._return_value
            if actual_ctx.is_script_exception_defined:
                actual_ctx._return_value = ScriptException(message)
            else:
                actual_ctx._return_value = UNSUPPORTED_SCRIPT_FEATURE_ERROR
                raise actual_ctx
            return actual_ctx._return_value

        def retry_operation(message=None, retry_after=None):
            if actual_ctx._return_value is not None:
                actual_ctx._return_value = ILLEGAL_CTX_OPERATION_ERROR
                raise actual_ctx._return_value
            actual_ctx.operation.retry(message=message,
                                       retry_after=retry_after)
            if actual_ctx.is_script_exception_defined:
                actual_ctx._return_value = ScriptException(message, retry=True)
            else:
                actual_ctx._return_value = UNSUPPORTED_SCRIPT_FEATURE_ERROR
                raise actual_ctx._return_value
            return actual_ctx._return_value

        actual_ctx.abort_operation = abort_operation
        actual_ctx.retry_operation = retry_operation

        def returns(_value):
            if actual_ctx._return_value is not None:
                actual_ctx._return_value = ILLEGAL_CTX_OPERATION_ERROR
                raise actual_ctx._return_value
            actual_ctx._return_value = _value
        actual_ctx.returns = returns

        actual_ctx._return_value = None
        original_download_resource = actual_ctx.download_resource

        def download_resource(resource_path, target_path=None):
            local_target_path = original_download_resource(resource_path)
            return fabric_put_in_remote_path(local_target_path, target_path)

        actual_ctx.download_resource = download_resource

        original_download_resource_and_render = \
            actual_ctx.download_resource_and_render

        def download_resource_and_render(resource_path,
                                         target_path=None,
                                         template_variables=None):
            local_target_path = original_download_resource_and_render(
                resource_path,
                template_variables=template_variables)

            return fabric_put_in_remote_path(local_target_path, target_path)

        actual_ctx.download_resource_and_render = download_resource_and_render

        def fabric_put_in_remote_path(local_target_path, target_path):
            if target_path:
                remote_target_path = target_path
            else:
                remote_target_path = '{0}/{1}'.format(
                    remote_work_dir,
                    os.path.basename(local_target_path))
            fabric_api.put(local_target_path, remote_target_path)
            return remote_target_path

        def handle_script_result(script_result):
            if (actual_ctx.is_script_exception_defined and
               isinstance(script_result, ScriptException)):
                if script_result.retry:
                    return script_result
                else:
                    raise NonRecoverableError(str(script_result))
            # this happens when more than 1 ctx operation is invoked or
            # the plugin runs an unsupported feature on older Cloudify
            elif isinstance(script_result, RuntimeError):
                raise NonRecoverableError(str(script_result))
            # determine if this code runs during exception handling
            current_exception = sys.exc_info()[1]
            if current_exception:
                raise
            else:
                return script_result

        proxy = proxy_server.HTTPCtxProxy(actual_ctx, port=ctx_server_port)

        env_script = StringIO()
        env['PATH'] = '{0}:$PATH'.format(remote_ctx_dir)
        env[CTX_SOCKET_URL] = proxy.socket_url
        for key, value in env.iteritems():
            env_script.write('export {0}={1}\n'.format(key, value))
        env_script.write('chmod +x {0}\n'.format(remote_script_path))
        env_script.write('chmod +x {0}\n'.format(remote_ctx_path))
        # invoke sys.exc_clear() because handle_script_result
        # invokes sys.exc_info()
        sys.exc_clear()
        try:
            fabric_api.put(local_script_path, remote_script_path)
            fabric_api.put(env_script, remote_env_script_path)
            with fabric_context.cd(cwd):
                with tunnel.remote(proxy.port):
                    try:
                        fabric_api.run('source {0} && {1}'.format(
                            remote_env_script_path, command))
                    except FabricTaskError:
                        return handle_script_result(actual_ctx._return_value)
            return handle_script_result(actual_ctx._return_value)
        finally:
            proxy.close()
Exemplo n.º 8
0
def execute(script_path, ctx, process):
    on_posix = 'posix' in sys.builtin_module_names

    proxy = start_ctx_proxy(ctx, process)
    env = _get_process_environment(process, proxy)
    cwd = process.get('cwd')

    command_prefix = process.get('command_prefix')
    if command_prefix:
        command = '{0} {1}'.format(command_prefix, script_path)
    else:
        command = script_path

    args = process.get('args')
    if args:
        command = ' '.join([command] + args)

    # Figure out logging.

    log_stdout = process.get('log_stdout', True)
    log_stderr = process.get('log_stderr', True)
    stderr_to_stdout = process.get('stderr_to_stdout', False)

    ctx.logger.debug('log_stdout=%r, log_stderr=%r, stderr_to_stdout=%r',
                     log_stdout, log_stderr, stderr_to_stdout)

    if log_stderr:
        stderr_value = subprocess.STDOUT if stderr_to_stdout \
            else subprocess.PIPE
    else:
        stderr_value = None

    consume_stderr = stderr_value == subprocess.PIPE

    process = subprocess.Popen(args=command,
                               shell=True,
                               stdout=subprocess.PIPE if log_stdout else None,
                               stderr=stderr_value,
                               env=env,
                               cwd=cwd,
                               bufsize=1,
                               close_fds=on_posix)

    pid = process.pid
    ctx.logger.info('Process created, PID: {0}'.format(pid))

    stdout_consumer = stderr_consumer = None

    if log_stdout:
        stdout_consumer = OutputConsumer(
            process.stdout,
            ctx.logger,
            '<out> ',
            ctx=operation_ctx._get_current_object())
        ctx.logger.debug('Started consumer thread for stdout')
    if consume_stderr:
        stderr_consumer = OutputConsumer(
            process.stderr,
            ctx.logger,
            '<err> ',
            ctx=operation_ctx._get_current_object())
        ctx.logger.debug('Started consumer thread for stderr')

    log_counter = 0
    while True:
        process_ctx_request(proxy)
        return_code = process.poll()
        if return_code is not None:
            break
        time.sleep(POLL_LOOP_INTERVAL)

        log_counter += 1
        if log_counter == POLL_LOOP_LOG_ITERATIONS:
            log_counter = 0
            ctx.logger.info('Waiting for process {0} to end...'.format(pid))

    ctx.logger.info('Execution done (PID={0}, return_code={1}): {2}'.format(
        pid, return_code, command))

    try:
        proxy.close()
    except Exception:
        ctx.logger.warning('Failed closing context proxy', exc_info=True)
    else:
        ctx.logger.debug("Context proxy closed")

    for consumer, name in [(stdout_consumer, 'stdout'),
                           (stderr_consumer, 'stderr')]:
        if consumer:
            ctx.logger.debug('Joining consumer thread for %s', name)
            consumer.join()
            ctx.logger.debug('Consumer thread for %s ended', name)
        else:
            ctx.logger.debug('Consumer thread for %s not created; not joining',
                             name)

    # happens when more than 1 ctx result command is used
    if isinstance(ctx._return_value, RuntimeError):
        raise NonRecoverableError(str(ctx._return_value))
    elif return_code != 0:
        if not (ctx.is_script_exception_defined
                and isinstance(ctx._return_value, ScriptException)):
            if log_stdout:
                stdout = ''.join(stdout_consumer.output)
            else:
                stdout = process.stdout.read().decode('utf-8', 'replace')
            if stderr_to_stdout:
                stderr = None
            elif log_stderr:
                stderr = ''.join(stderr_consumer.output)
            else:
                stderr = process.stderr.read().decode('utf-8', 'replace')
            raise ProcessException(command, return_code, stdout, stderr)
Exemplo n.º 9
0
def run_script(script_path, fabric_env=None, process=None, **kwargs):

    if not process:
        process = {}
    process = _create_process_config(process, kwargs)
    base_dir = process.get('base_dir', DEFAULT_BASE_DIR)
    ctx_server_port = process.get('ctx_server_port')

    proxy_client_path = proxy_client.__file__
    if proxy_client_path.endswith('.pyc'):
        proxy_client_path = proxy_client_path[:-1]
    local_script_path = get_script(ctx.download_resource, script_path)
    base_script_path = os.path.basename(local_script_path)
    remote_ctx_dir = base_dir
    remote_ctx_path = '{0}/ctx'.format(remote_ctx_dir)
    remote_scripts_dir = '{0}/scripts'.format(remote_ctx_dir)
    remote_work_dir = '{0}/work'.format(remote_ctx_dir)
    remote_env_script_path = '{0}/env-{1}'.format(remote_scripts_dir,
                                                  base_script_path)
    remote_script_path = '{0}/{1}'.format(remote_scripts_dir,
                                          base_script_path)

    env = process.get('env', {})
    cwd = process.get('cwd', remote_work_dir)
    args = process.get('args')
    command_prefix = process.get('command_prefix')

    command = remote_script_path
    if command_prefix:
        command = '{0} {1}'.format(command_prefix, command)
    if args:
        command = ' '.join([command] + args)

    with fabric_api.settings(**_fabric_env(fabric_env, warn_only=False)):
        if not fabric_files.exists(remote_ctx_dir):
            # there may be race conditions with other operations that
            # may be running in parallel, so we pass -p to make sure
            # we get 0 exit code if the directory already exists
            fabric_api.run('mkdir -p {0}'.format(remote_scripts_dir))
            fabric_api.run('mkdir -p {0}'.format(remote_work_dir))
            fabric_api.put(proxy_client_path, remote_ctx_path)

        actual_ctx = ctx._get_current_object()

        def returns(_value):
            actual_ctx._return_value = _value
        actual_ctx._return_value = None
        actual_ctx.returns = returns

        original_download_resource = actual_ctx.download_resource

        def download_resource(resource_path, target_path=None):
            local_target_path = original_download_resource(resource_path)
            if target_path:
                remote_target_path = target_path
            else:
                remote_target_path = '{0}/{1}'.format(
                    remote_work_dir,
                    os.path.basename(local_target_path))
            fabric_api.put(local_target_path, remote_target_path)
            return remote_target_path
        actual_ctx.download_resource = download_resource

        proxy = proxy_server.HTTPCtxProxy(actual_ctx, port=ctx_server_port)

        env_script = StringIO()
        env['PATH'] = '{0}:$PATH'.format(remote_ctx_dir)
        env[CTX_SOCKET_URL] = proxy.socket_url
        for key, value in env.iteritems():
            env_script.write('export {0}={1}\n'.format(key, value))
        env_script.write('chmod +x {0}\n'.format(remote_script_path))
        env_script.write('chmod +x {0}\n'.format(remote_ctx_path))
        try:
            fabric_api.put(local_script_path, remote_script_path)
            fabric_api.put(env_script, remote_env_script_path)
            with fabric_context.cd(cwd):
                with tunnel.remote(proxy.port):
                    fabric_api.run('source {0} && {1}'.format(
                        remote_env_script_path, command))
            return actual_ctx._return_value
        finally:
            proxy.close()
Exemplo n.º 10
0
def run_script(script_path, fabric_env=None, process=None, **kwargs):

    if not process:
        process = {}
    process = _create_process_config(process, kwargs)
    base_dir = process.get('base_dir', DEFAULT_BASE_DIR)
    ctx_server_port = process.get('ctx_server_port')

    proxy_client_path = proxy_client.__file__
    if proxy_client_path.endswith('.pyc'):
        proxy_client_path = proxy_client_path[:-1]
    local_script_path = get_script(ctx.download_resource, script_path)
    base_script_path = os.path.basename(local_script_path)
    remote_ctx_dir = base_dir
    remote_ctx_path = '{0}/ctx'.format(remote_ctx_dir)
    remote_scripts_dir = '{0}/scripts'.format(remote_ctx_dir)
    remote_work_dir = '{0}/work'.format(remote_ctx_dir)
    remote_env_script_path = '{0}/env-{1}'.format(remote_scripts_dir,
                                                  base_script_path)
    remote_script_path = '{0}/{1}'.format(remote_scripts_dir, base_script_path)

    env = process.get('env', {})
    cwd = process.get('cwd', remote_work_dir)
    args = process.get('args')
    command_prefix = process.get('command_prefix')

    command = remote_script_path
    if command_prefix:
        command = '{0} {1}'.format(command_prefix, command)
    if args:
        command = ' '.join([command] + args)

    with fabric_api.settings(**_fabric_env(fabric_env, warn_only=False)):
        if not fabric_files.exists(remote_ctx_dir):
            # there may be race conditions with other operations that
            # may be running in parallel, so we pass -p to make sure
            # we get 0 exit code if the directory already exists
            fabric_api.run('mkdir -p {0}'.format(remote_scripts_dir))
            fabric_api.run('mkdir -p {0}'.format(remote_work_dir))
            fabric_api.put(proxy_client_path, remote_ctx_path)

        actual_ctx = ctx._get_current_object()

        def returns(_value):
            actual_ctx._return_value = _value

        actual_ctx._return_value = None
        actual_ctx.returns = returns

        original_download_resource = actual_ctx.download_resource

        def download_resource(resource_path, target_path=None):
            local_target_path = original_download_resource(
                resource_path, target_path)
            if target_path:
                remote_target_path = target_path
            else:
                remote_target_path = '{0}/{1}'.format(
                    remote_work_dir, os.path.basename(local_target_path))
            fabric_api.put(local_target_path, remote_target_path)
            return remote_target_path

        actual_ctx.download_resource = download_resource

        proxy = proxy_server.HTTPCtxProxy(actual_ctx, port=ctx_server_port)

        env_script = StringIO()
        env['PATH'] = '{0}:$PATH'.format(remote_ctx_dir)
        env[CTX_SOCKET_URL] = proxy.socket_url
        for key, value in env.iteritems():
            env_script.write('export {0}={1}\n'.format(key, value))
        env_script.write('chmod +x {0}\n'.format(remote_script_path))
        env_script.write('chmod +x {0}\n'.format(remote_ctx_path))
        try:
            fabric_api.put(local_script_path, remote_script_path)
            fabric_api.put(env_script, remote_env_script_path)
            with fabric_context.cd(cwd):
                with fabric_context.remote_tunnel(proxy.port):
                    fabric_api.run('source {0} && {1}'.format(
                        remote_env_script_path, command))
            return actual_ctx._return_value
        finally:
            proxy.close()
Exemplo n.º 11
0
def run_script(script_path, fabric_env=None, process=None, **kwargs):

    if not process:
        process = {}
    process = _create_process_config(process, kwargs)
    base_dir = process.get('base_dir', DEFAULT_BASE_DIR)
    ctx_server_port = process.get('ctx_server_port')

    proxy_client_path = proxy_client.__file__
    if proxy_client_path.endswith('.pyc'):
        proxy_client_path = proxy_client_path[:-1]
    local_ctx_sh_path = os.path.join(_get_bin_dir(), 'ctx-sh')
    local_script_path = get_script(ctx.download_resource, script_path)
    base_script_path = os.path.basename(local_script_path)
    remote_ctx_dir = base_dir
    remote_ctx_path = '{0}/ctx'.format(remote_ctx_dir)
    remote_ctx_sh_path = '{0}/ctx-sh'.format(remote_ctx_dir)
    remote_scripts_dir = '{0}/scripts'.format(remote_ctx_dir)
    remote_work_dir = '{0}/work'.format(remote_ctx_dir)
    remote_path_suffix = '{0}-{1}'.format(base_script_path,
                                          utils.id_generator(size=8))
    remote_env_script_path = '{0}/env-{1}'.format(remote_scripts_dir,
                                                  remote_path_suffix)
    remote_script_path = '{0}/{1}'.format(remote_scripts_dir,
                                          remote_path_suffix)

    env = process.get('env', {})
    cwd = process.get('cwd', remote_work_dir)
    args = process.get('args')
    command_prefix = process.get('command_prefix')

    command = remote_script_path
    if command_prefix:
        command = '{0} {1}'.format(command_prefix, command)
    if args:
        command = ' '.join([command] + args)

    with fabric_api.settings(**_fabric_env(fabric_env, warn_only=False)):
        # the remote host must have ctx and any related files before
        # running any fabric scripts
        if not fabric_files.exists(remote_ctx_path):

            # there may be race conditions with other operations that
            # may be running in parallel, so we pass -p to make sure
            # we get 0 exit code if the directory already exists
            fabric_api.run('mkdir -p {0}'.format(remote_scripts_dir))
            fabric_api.run('mkdir -p {0}'.format(remote_work_dir))

            # this file has to be present before using ctx
            fabric_api.put(local_ctx_sh_path, remote_ctx_sh_path)

            fabric_api.put(proxy_client_path, remote_ctx_path)

        actual_ctx = ctx._get_current_object()

        actual_ctx.is_script_exception_defined = ScriptException is not None

        def abort_operation(message=None):
            if actual_ctx._return_value is not None:
                actual_ctx._return_value = ILLEGAL_CTX_OPERATION_ERROR
                raise actual_ctx._return_value
            if actual_ctx.is_script_exception_defined:
                actual_ctx._return_value = ScriptException(message)
            else:
                actual_ctx._return_value = UNSUPPORTED_SCRIPT_FEATURE_ERROR
                raise actual_ctx
            return actual_ctx._return_value

        def retry_operation(message=None, retry_after=None):
            if actual_ctx._return_value is not None:
                actual_ctx._return_value = ILLEGAL_CTX_OPERATION_ERROR
                raise actual_ctx._return_value
            actual_ctx.operation.retry(message=message,
                                       retry_after=retry_after)
            if actual_ctx.is_script_exception_defined:
                actual_ctx._return_value = ScriptException(message, retry=True)
            else:
                actual_ctx._return_value = UNSUPPORTED_SCRIPT_FEATURE_ERROR
                raise actual_ctx._return_value
            return actual_ctx._return_value

        actual_ctx.abort_operation = abort_operation
        actual_ctx.retry_operation = retry_operation

        def returns(_value):
            if actual_ctx._return_value is not None:
                actual_ctx._return_value = ILLEGAL_CTX_OPERATION_ERROR
                raise actual_ctx._return_value
            actual_ctx._return_value = _value

        actual_ctx.returns = returns

        actual_ctx._return_value = None
        original_download_resource = actual_ctx.download_resource

        def download_resource(resource_path, target_path=None):
            local_target_path = original_download_resource(resource_path)
            return fabric_put_in_remote_path(local_target_path, target_path)

        actual_ctx.download_resource = download_resource

        original_download_resource_and_render = \
            actual_ctx.download_resource_and_render

        def download_resource_and_render(resource_path,
                                         target_path=None,
                                         template_variables=None):
            local_target_path = original_download_resource_and_render(
                resource_path, template_variables=template_variables)

            return fabric_put_in_remote_path(local_target_path, target_path)

        actual_ctx.download_resource_and_render = download_resource_and_render

        def fabric_put_in_remote_path(local_target_path, target_path):
            if target_path:
                remote_target_path = target_path
            else:
                remote_target_path = '{0}/{1}'.format(
                    remote_work_dir, os.path.basename(local_target_path))
            fabric_api.put(local_target_path, remote_target_path)
            return remote_target_path

        def handle_script_result(script_result):
            if (actual_ctx.is_script_exception_defined
                    and isinstance(script_result, ScriptException)):
                if script_result.retry:
                    return script_result
                else:
                    raise NonRecoverableError(str(script_result))
            # this happens when more than 1 ctx operation is invoked or
            # the plugin runs an unsupported feature on older Cloudify
            elif isinstance(script_result, RuntimeError):
                raise NonRecoverableError(str(script_result))
            # determine if this code runs during exception handling
            current_exception = sys.exc_info()[1]
            if current_exception:
                raise
            else:
                return script_result

        proxy = proxy_server.HTTPCtxProxy(actual_ctx, port=ctx_server_port)

        env_script = StringIO()
        env['PATH'] = '{0}:$PATH'.format(remote_ctx_dir)
        env[CTX_SOCKET_URL] = proxy.socket_url
        for key, value in env.iteritems():
            env_script.write('export {0}={1}\n'.format(key, value))
        env_script.write('chmod +x {0}\n'.format(remote_script_path))
        env_script.write('chmod +x {0}\n'.format(remote_ctx_path))
        # invoke sys.exc_clear() because handle_script_result
        # invokes sys.exc_info()
        sys.exc_clear()
        try:
            fabric_api.put(local_script_path, remote_script_path)
            fabric_api.put(env_script, remote_env_script_path)
            with fabric_context.cd(cwd):
                with tunnel.remote(proxy.port):
                    try:
                        fabric_api.run('source {0} && {1}'.format(
                            remote_env_script_path, command))
                    except FabricTaskError:
                        return handle_script_result(actual_ctx._return_value)
            return handle_script_result(actual_ctx._return_value)
        finally:
            proxy.close()