Currently, when a new process is spawned on Windows, the subprocess module is used. When it will run from inside a greenthread it will try to set non-blocking mode on the anonymous pipes that it creates for the process stdin, stdout and stderr. This is not supported in Windows. This patch changes the usage of subprocess.Popen with CreateProcess and implements non-blocking I/O using NamedPipes. The files obtained from CreateFile on the NamedPipe will be passed as stdin, stdout and stderr to the new process spawned with CreateProcess. In order to communicate with the new process, overlapped structures are used to ensure that the greenthread will not be blocked. Change-Id: I173b2e20275e4f89065c657bfcd0128ab4d1d3bc
77 lines
2.3 KiB
Python
77 lines
2.3 KiB
Python
# Copyright 2015 Cloudbase Solutions.
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import os
|
|
|
|
from neutron_lib.utils import helpers
|
|
from oslo_log import log as logging
|
|
from oslo_utils import encodeutils
|
|
|
|
from neutron._i18n import _
|
|
from neutron.agent.windows import winutils
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
def create_process(cmd, addl_env=None):
|
|
cmd = list(map(str, cmd))
|
|
|
|
LOG.debug("Running command: %s", cmd)
|
|
env = os.environ.copy()
|
|
if addl_env:
|
|
env.update(addl_env)
|
|
|
|
obj = winutils.ProcessWithNamedPipes(cmd, env)
|
|
|
|
return obj, cmd
|
|
|
|
|
|
def execute(cmd, process_input=None, addl_env=None,
|
|
check_exit_code=True, return_stderr=False, log_fail_as_error=True,
|
|
extra_ok_codes=None, run_as_root=False, do_decode=True):
|
|
|
|
if process_input is not None:
|
|
_process_input = encodeutils.to_utf8(process_input)
|
|
else:
|
|
_process_input = None
|
|
obj, cmd = create_process(cmd, addl_env=addl_env)
|
|
_stdout, _stderr = obj.communicate(_process_input)
|
|
obj.stdin.close()
|
|
_stdout = helpers.safe_decode_utf8(_stdout)
|
|
_stderr = helpers.safe_decode_utf8(_stderr)
|
|
|
|
m = _("\nCommand: %(cmd)s\nExit code: %(code)s\nStdin: %(stdin)s\n"
|
|
"Stdout: %(stdout)s\nStderr: %(stderr)s") % \
|
|
{'cmd': cmd,
|
|
'code': obj.returncode,
|
|
'stdin': process_input or '',
|
|
'stdout': _stdout,
|
|
'stderr': _stderr}
|
|
|
|
extra_ok_codes = extra_ok_codes or []
|
|
if obj.returncode and obj.returncode in extra_ok_codes:
|
|
obj.returncode = None
|
|
|
|
log_msg = m.strip().replace('\n', '; ')
|
|
if obj.returncode and log_fail_as_error:
|
|
LOG.error(log_msg)
|
|
else:
|
|
LOG.debug(log_msg)
|
|
|
|
if obj.returncode and check_exit_code:
|
|
raise RuntimeError(m)
|
|
|
|
return (_stdout, _stderr) if return_stderr else _stdout
|