Files
neutron/neutron/agent/windows/utils.py
Alin Balutoiu 1ab4577a5f Replace subprocess.Popen with CreateProcess on Windows
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
2017-04-21 12:10:01 +03:00

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