Remove unused eventlet code

- Remove the `tacker.agent` package. Its only module,
  agent/linux/utils.py, is not referenced anywhere in tacker.
- Remove `subprocess_popen()` from `tacker.common.utils`; the removed
  agent module was its only caller. The `subprocess` import it needed
  goes away with it.
- Import `sleep()` used by the cooperative readers directly from the
  time module instead of the eventlet fallback. `time.sleep(0)` yields
  the GIL, so the readers still allow other threads to be scheduled
  between chunks.
- Remove the GreenPool instantiated but never used by NfvoPlugin.

Signed-off-by: Yasufumi Ogawa <yasufum.o@gmail.com>
Change-Id: I408e264a271311a7fc7ea21e0b4873b80d92b064
This commit is contained in:
Yasufumi Ogawa committed 2026-09-02 22:24:03 +09:00
1 parent dff234bdcb
commit 2315a0de13
8 files changed
+9 -267

No files matched your search

View File
Whitespace-only changes.
View File
Whitespace-only changes.
-95
View File
@@ -1,95 +0,0 @@
# Copyright 2012 Locaweb.
# 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
import shlex
import subprocess
import time
from oslo_log import log as logging
from oslo_utils import excutils
from tacker._i18n import _
from tacker.common import utils
LOG = logging.getLogger(__name__)
def create_process(cmd, root_helper=None, addl_env=None,
debuglog=True):
"""Create a process object for the given command.
The return value will be a tuple of the process object and the
list of command arguments used to create it.
"""
if root_helper:
cmd = shlex.split(root_helper) + cmd
cmd = map(str, cmd)
if debuglog:
LOG.debug("Running command: %s", cmd)
env = os.environ.copy()
if addl_env:
env.update(addl_env)
obj = utils.subprocess_popen(cmd, shell=False,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=env)
return obj, cmd
def execute(cmd, root_helper=None, process_input=None, addl_env=None,
check_exit_code=True, return_stderr=False, debuglog=True):
# Note(gongysh) not use log_levels in config file because
# some other codes that are not in a loop probably need the debug log
try:
obj, cmd = create_process(cmd, root_helper=root_helper,
addl_env=addl_env, debuglog=debuglog)
_stdout, _stderr = (process_input and
obj.communicate(process_input) or
obj.communicate())
obj.stdin.close()
m = _("\nCommand: %(cmd)s\nExit code: %(code)s\nStdout: %(stdout)r\n"
"Stderr: %(stderr)r") % {'cmd': cmd, 'code': obj.returncode,
'stdout': _stdout, 'stderr': _stderr}
if obj.returncode:
LOG.error(m)
if check_exit_code:
raise RuntimeError(m)
elif debuglog:
LOG.debug(m)
finally:
time.sleep(0)
return return_stderr and (_stdout, _stderr) or _stdout
def find_child_pids(pid):
"""Retrieve a list of the pids of child processes of the given pid."""
try:
raw_pids = execute(['ps', '--ppid', pid, '-o', 'pid='])
except RuntimeError as e:
# Unexpected errors are the responsibility of the caller
with excutils.save_and_reraise_exception() as ctxt:
# Exception has already been logged by execute
no_children_found = 'Exit code: 1' in str(e)
if no_children_found:
ctxt.reraise = False
return []
return [x.strip() for x in raw_pids.split('\n') if x.strip()]
+9 -20
View File
@@ -26,8 +26,8 @@ import math
import os
import re
import socket
import subprocess
import sys
from time import sleep
from urllib import parse as urlparse
import netaddr
@@ -38,11 +38,6 @@ from oslo_utils import excutils
from oslo_utils import importutils
from stevedore import driver
try:
from eventlet import sleep
except ImportError:
from time import sleep
from tacker._i18n import _
from tacker.common import constants as q_const
from tacker.common import exceptions
@@ -143,12 +138,6 @@ def find_config_file(options, config_file):
return cfg_file
def subprocess_popen(args, stdin=None, stdout=None, stderr=None, shell=False,
env=None):
return subprocess.Popen(args, shell=shell, stdin=stdin, stdout=stdout,
stderr=stderr, close_fds=True, env=env)
def get_hostname():
return socket.gethostname()
@@ -282,10 +271,10 @@ def expects_func_args(*args):
def cooperative_iter(iter):
"""Prevent eventlet thread starvation during iteration
"""Prevent thread starvation during iteration
Return an iterator which schedules after each
iteration. This can prevent eventlet thread starvation.
iteration. This can prevent thread starvation.
:param iter: an iterator to wrap
"""
@@ -300,10 +289,10 @@ def cooperative_iter(iter):
def cooperative_read(fd):
"""Prevent eventlet thread starvationafter each read operation.
"""Prevent thread starvation after each read operation.
Wrap a file descriptor's read with a partial function which schedules
after each read. This can prevent eventlet thread starvation.
after each read. This can prevent thread starvation.
:param fd: a file descriptor to wrap
"""
@@ -502,13 +491,13 @@ def json_merge_patch(target, patch):
class CooperativeReader(object):
"""An eventlet thread friendly class for reading in image data.
"""A thread friendly class for reading in image data.
When accessing data either through the iterator or the read method
we perform a sleep to allow a co-operative yield. When there is more than
one image being uploaded/downloaded this prevents eventlet thread
starvation, ie allows all threads to be scheduled periodically rather than
having the same thread be continuously active.
one image being uploaded/downloaded this prevents thread starvation,
ie allows all threads to be scheduled periodically rather than having
the same thread be continuously active.
"""
def __init__(self, fd):
"""Construct an CooperativeReader object.
-2
View File
@@ -16,7 +16,6 @@
import copy
import eventlet
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import excutils
@@ -60,7 +59,6 @@ class NfvoPlugin(nfvo_db_plugin.NfvoPluginDb):
def __init__(self):
super(NfvoPlugin, self).__init__()
self._pool = eventlet.GreenPool()
self._vim_drivers = driver_manager.DriverManager(
'tacker.nfvo.vim.drivers',
cfg.CONF.nfvo_vim.vim_drivers)
View File
Whitespace-only changes.
Whitespace-only changes.
-150
View File
@@ -1,150 +0,0 @@
# Copyright 2012, VMware, Inc.
#
# 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.
from unittest import mock
import oslo_i18n
from tacker.agent.linux import utils
from tacker.tests import base
_marker = object()
class AgentUtilsExecuteTest(base.BaseTestCase):
def setUp(self):
super(AgentUtilsExecuteTest, self).setUp()
self.test_file = self.get_temp_file_path('test_execute.tmp')
open(self.test_file, 'w').close()
self.process = mock.patch(
'tacker.common.utils.subprocess.Popen'
).start()
self.process.return_value.returncode = 0
self.mock_popen = self.process.return_value.communicate
def test_without_helper(self):
expected = "%s\n" % self.test_file
self.mock_popen.return_value = [expected, ""]
result = utils.execute(["ls", self.test_file])
self.assertEqual(result, expected)
def test_with_helper(self):
expected = "ls %s\n" % self.test_file
self.mock_popen.return_value = [expected, ""]
result = utils.execute(["ls", self.test_file], root_helper='echo')
self.assertEqual(result, expected)
def test_stderr_true(self):
expected = "%s\n" % self.test_file
self.mock_popen.return_value = [expected, ""]
out = utils.execute(["ls", self.test_file], return_stderr=True)
self.assertIsInstance(out, tuple)
self.assertEqual(out, (expected, ""))
def test_check_exit_code(self):
self.mock_popen.return_value = ["", ""]
stdout = utils.execute(["ls", self.test_file[:-1]],
check_exit_code=False)
self.assertEqual("", stdout)
def test_execute_raises(self):
self.mock_popen.side_effect = RuntimeError
self.assertRaises(RuntimeError, utils.execute,
["ls", self.test_file[:-1]])
def test_process_input(self):
expected = "%s\n" % self.test_file[:-1]
self.mock_popen.return_value = [expected, ""]
result = utils.execute(["cat"], process_input="%s\n" %
self.test_file[:-1])
self.assertEqual(result, expected)
def test_with_addl_env(self):
expected = "%s\n" % self.test_file
self.mock_popen.return_value = [expected, ""]
result = utils.execute(["ls", self.test_file],
addl_env={'foo': 'bar'})
self.assertEqual(result, expected)
def test_return_code_log_error_raise_runtime(self):
self.mock_popen.return_value = ('', '')
self.process.return_value.returncode = 1
with mock.patch.object(utils, 'LOG') as log:
self.assertRaises(RuntimeError, utils.execute,
['ls'])
self.assertTrue(log.error.called)
def test_return_code_log_error_no_raise_runtime(self):
self.mock_popen.return_value = ('', '')
self.process.return_value.returncode = 1
with mock.patch.object(utils, 'LOG') as log:
utils.execute(['ls'], check_exit_code=False)
self.assertTrue(log.error.called)
def test_return_code_log_debug(self):
self.mock_popen.return_value = ('', '')
with mock.patch.object(utils, 'LOG') as log:
utils.execute(['ls'])
self.assertTrue(log.debug.called)
def test_return_code_log_error_change_locale(self):
ja_output = 'std_out in Japanese'
ja_error = 'std_err in Japanese'
ja_message_out = oslo_i18n._message.Message(ja_output)
ja_message_err = oslo_i18n._message.Message(ja_error)
ja_translate_out = oslo_i18n._translate.translate(ja_message_out, 'ja')
ja_translate_err = oslo_i18n._translate.translate(ja_message_err, 'ja')
self.mock_popen.return_value = (ja_translate_out, ja_translate_err)
self.process.return_value.returncode = 1
with mock.patch.object(utils, 'LOG') as log:
utils.execute(['ls'], check_exit_code=False)
self.assertIn(ja_translate_out, str(log.error.call_args_list))
self.assertIn(ja_translate_err, str(log.error.call_args_list))
def test_return_code_raise_runtime_log_fail_as_error(self):
self.mock_popen.return_value = ('', '')
self.process.return_value.returncode = 1
with mock.patch.object(utils, 'LOG') as log:
self.assertRaises(RuntimeError, utils.execute,
['ls'])
self.assertTrue(log.error.called)
def test_encode_process_input(self):
bytes_idata = bytes("%s\n" % self.test_file[:-1], 'utf-8')
bytes_odata = bytes("%s\n" % self.test_file, 'utf-8')
self.mock_popen.return_value = [bytes_odata, b'']
result = utils.execute(['cat'], process_input=bytes_idata)
self.mock_popen.assert_called_once_with(bytes_idata)
self.assertEqual(bytes_odata, result)
def test_return_str_data(self):
str_data = "%s\n" % self.test_file
self.mock_popen.return_value = [str_data, '']
result = utils.execute(['ls', self.test_file], return_stderr=True)
self.assertEqual((str_data, ''), result)
class AgentUtilsExecuteEncodeTest(base.BaseTestCase):
def setUp(self):
super(AgentUtilsExecuteEncodeTest, self).setUp()
self.test_file = self.get_temp_file_path('test_execute.tmp')
open(self.test_file, 'w').close()
def test_decode_return_data(self):
str_data = bytes("%s\n" % self.test_file, 'utf-8')
result = utils.execute(['ls', self.test_file], return_stderr=True)
self.assertEqual((str_data, bytes('', 'utf-8')), result)