bbf57b1c2a
This change removes eventlet usage and explicitly invokes threading instead of eventlet. To do this, it also revises signal handling because this change also moves from an all in one process to a multi-process runtime model. The side effect of this is an increased memory footprint, depending on the process launch model. Change-Id: I184245d67edb1a2543aa24654836392f38777d71 Signed-off-by: Julia Kreger <juliaashleykreger@gmail.com>
178 lines
7.2 KiB
Python
178 lines
7.2 KiB
Python
# -*- encoding: utf-8 -*-
|
|
#
|
|
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
|
# 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.
|
|
|
|
"""
|
|
The Ironic Management Service
|
|
"""
|
|
|
|
import sys
|
|
|
|
from oslo_config import cfg
|
|
from oslo_log import log
|
|
from oslo_service import service
|
|
|
|
from ironic.command import utils as command_utils
|
|
from ironic.common import service as ironic_service
|
|
from ironic.common import utils
|
|
from ironic.conductor import rpc_service
|
|
|
|
CONF = cfg.CONF
|
|
|
|
LOG = log.getLogger(__name__)
|
|
|
|
|
|
def warn_about_unsafe_shred_parameters(conf):
|
|
iterations = conf.deploy.shred_random_overwrite_iterations
|
|
overwrite_with_zeros = conf.deploy.shred_final_overwrite_with_zeros
|
|
if iterations == 0 and overwrite_with_zeros is False:
|
|
LOG.warning('With shred_random_overwrite_iterations set to 0 and '
|
|
'shred_final_overwrite_with_zeros set to False, disks '
|
|
'may NOT be shredded at all, unless they support ATA '
|
|
'Secure Erase. This is a possible SECURITY ISSUE!')
|
|
|
|
|
|
def warn_about_sqlite():
|
|
# We are intentionally calling the helper here to ensure it caches
|
|
# for all future calls.
|
|
if utils.is_ironic_using_sqlite():
|
|
LOG.warning('Ironic has been configured to utilize SQLite. '
|
|
'This has some restrictions and impacts. You must run '
|
|
'as as a single combined ironic process, and some '
|
|
'internal mechanisms do not execute such as the hash '
|
|
'ring will remain static and the conductor\'s '
|
|
'``last_updated`` field will also not update. This is '
|
|
'in order to minimize database locking issues present '
|
|
'as a result of SQLAlchemy 2.0 and the removal of '
|
|
'autocommit support.')
|
|
|
|
|
|
def warn_about_max_wait_parameters(conf):
|
|
max_wait = conf.conductor.max_conductor_wait_step_seconds
|
|
max_deploy_timeout = conf.conductor.deploy_callback_timeout
|
|
max_clean_timeout = conf.conductor.clean_callback_timeout
|
|
error_with = None
|
|
if max_wait >= max_deploy_timeout:
|
|
error_with = 'deploy_callback_timeout'
|
|
if max_wait >= max_clean_timeout:
|
|
error_with = 'clean_callback_timeout'
|
|
if error_with:
|
|
LOG.warning('The [conductor]max_conductor_wait_step_seconds '
|
|
'configuration parameter exceeds the value of '
|
|
'[conductor]%s, which could create a condition where '
|
|
'tasks may timeout. Ironic recommends a low default '
|
|
'value for [conductor]max_conductor_wait_step_seconds '
|
|
'please re-evaluate your configuration.', error_with)
|
|
|
|
|
|
def warn_about_inconsistent_kernel_ramdisk(conf):
|
|
"""Check consistency of configurations around kernels and ramdisks
|
|
|
|
This method logs a warning if any of the paired
|
|
CONF.conductor.*_ramdisk_by_arch and CONF.conductor.*_kernel_by_arch
|
|
config dictionaries are inconsistent -- such as having a kernel configured
|
|
for aarch64 without having a ramdisk configured.
|
|
|
|
:param conf: an ironic.conf.CONF oslo.config object
|
|
:returns: None
|
|
"""
|
|
config_pairs = [
|
|
('deploy_kernel_by_arch', 'deploy_ramdisk_by_arch', 'provisioning'),
|
|
('rescue_kernel_by_arch', 'rescue_ramdisk_by_arch', 'rescue'),
|
|
]
|
|
|
|
for kernel_opt, ramdisk_opt, operation in config_pairs:
|
|
kernel_dict = getattr(conf.conductor, kernel_opt)
|
|
ramdisk_dict = getattr(conf.conductor, ramdisk_opt)
|
|
|
|
kernel_arches = set(kernel_dict.keys())
|
|
ramdisk_arches = set(ramdisk_dict.keys())
|
|
|
|
kernel_only = kernel_arches - ramdisk_arches
|
|
ramdisk_only = ramdisk_arches - kernel_arches
|
|
|
|
if kernel_only:
|
|
LOG.warning('The [conductor]%s configuration has entries for '
|
|
'architectures %s that are missing from '
|
|
'[conductor]%s. This may result in failed %s '
|
|
'operations for these architectures.',
|
|
kernel_opt, ', '.join(sorted(kernel_only)),
|
|
ramdisk_opt, operation)
|
|
|
|
if ramdisk_only:
|
|
LOG.warning('The [conductor]%s configuration has entries for '
|
|
'architectures %s that are missing from '
|
|
'[conductor]%s. This may result in failed %s '
|
|
'operations for these architectures.',
|
|
ramdisk_opt, ', '.join(sorted(ramdisk_only)),
|
|
kernel_opt, operation)
|
|
|
|
|
|
def issue_startup_warnings(conf):
|
|
warn_about_unsafe_shred_parameters(conf)
|
|
warn_about_sqlite()
|
|
warn_about_max_wait_parameters(conf)
|
|
warn_about_inconsistent_kernel_ramdisk(conf)
|
|
|
|
|
|
def main():
|
|
# NOTE(lucasagomes): Safeguard to prevent 'ironic.conductor.manager'
|
|
# from being imported prior to the configuration options being loaded.
|
|
# If this happened, the periodic decorators would always use the
|
|
# default values of the options instead of the configured ones. For
|
|
# more information see: https://bugs.launchpad.net/ironic/+bug/1562258
|
|
# and https://bugs.launchpad.net/ironic/+bug/1279774.
|
|
assert 'ironic.conductor.manager' not in sys.modules
|
|
|
|
# Parse config file and command line options, then start logging
|
|
ironic_service.prepare_service('ironic_conductor', sys.argv)
|
|
ironic_service.ensure_rpc_transport(CONF)
|
|
|
|
mgr = rpc_service.RPCService(CONF.host,
|
|
'ironic.conductor.manager',
|
|
'ConductorManager')
|
|
|
|
issue_startup_warnings(CONF)
|
|
|
|
# Ultimately this returns a ServiceLauncher class which has a _manager
|
|
# object (cotyledon), and where launch_service has been invoked which
|
|
# adds an instance of the service to the _manager object.
|
|
launcher = service.launch(CONF, mgr, restart_method='mutate')
|
|
|
|
# The approach above also is for a single application where we then start a
|
|
# worker process.
|
|
|
|
# TODO(TheJulia): At this location, we're missing signaling, but where
|
|
# the signaling needs to be is past a spawn() call triggered by wait().
|
|
# What signaling here would make sense is signal handling to force this
|
|
# process to exit.
|
|
|
|
# TODO(TheJulia): So, the tl;dr of execution is wait() triggers the
|
|
# process launch, fork, and then other actions.
|
|
# This is in cotyledon where the Service Manager run() method is called
|
|
# https://github.com/sileht/cotyledon/blob/main/cotyledon/_service_manager.py#L240
|
|
# which then will wait forever and never return.
|
|
|
|
# Set override signals.
|
|
command_utils.handle_signal()
|
|
|
|
# Start the processes!
|
|
sys.exit(launcher.wait())
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|