def process_cli_result(stdout: str = None, stderr: str = None):
    res = None
    try:
        try:
            res = serialize.loads(stdout) if stdout else {}
        except errors.PrvsnrTypeDecodeError:
            logger.exception('Failed to decode provisioner output')
            res = serialize.loads(stdout, strict=False)
    except Exception:
        logger.exception(f"Unexpected result: {stdout}")

    if type(res) is not dict:
        raise errors.ProvisionerError(f'Unexpected result {stdout}')

    if 'exc' in res:
        logger.error("Provisioner CLI failed: {!r}".format(res['exc']))
        raise res['exc']
    else:
        try:
            return res['ret']
        except KeyError:
            logger.error(f"No return data found in '{stdout}', "
                         f"stderr: '{stderr}'")
            raise errors.ProvisionerError(
                f"No return data found in '{stdout}', "
                f"stderr: '{stderr}'")
def _run_cmd(cmd, env: Optional[Dict] = None, **kwargs):
    # Note. we update a copy of current process env since
    #       subprocess.run will replace the env of the current
    #       process
    if env is not None:
        env_copy = os.environ.copy()
        env_copy.update(env)
        env = env_copy

    try:
        logger.debug(f"Executing command: {cmd}")
        res = subprocess.run(cmd, env=env, **kwargs)
    # subprocess.run fails expectedly
    except subprocess.CalledProcessError as exc:
        return process_cli_result(exc.stdout, exc.stderr)
    # subprocess.run fails unexpectedly
    except Exception as exc:
        logger.exception("Failed to execute command")
        raise errors.ProvisionerError(repr(exc)) from exc
    else:
        return process_cli_result(res.stdout, res.stderr)