Support --heat-type=full

Add a new "full" ephemeral heat type which in addition to launching the
heat pod, will also launch an infra pod containing ephemeral instances
of mysql and rabbitmq. When using --heat-type=full, no undercloud
installation is necessary at all (if using deployed-server).

Change-Id: Ia6d823aa3f994d4ce05ea5c8940ec43c291d1add
Signed-off-by: James Slagle <jslagle@redhat.com>
This commit is contained in:
James Slagle 2021-06-09 07:26:47 -04:00
parent 723bf34caf
commit 6378a4d833
13 changed files with 480 additions and 87 deletions

View File

@ -0,0 +1,69 @@
[client]
port = 3306
socket = /var/lib/mysql/mysql.sock
[isamchk]
key_buffer_size = 16M
[mysqld]
wsrep_provider=none
basedir = /usr
bind-address = DATABASE_IP
connect_timeout = 60
datadir = /var/lib/mysql
expire_logs_days = 10
innodb_buffer_pool_size = 1G
innodb_file_per_table = ON
innodb_strict_mode = OFF
key_buffer_size = 16M
log-error = /var/log/mariadb/mariadb.log
log_warnings = 1
max_allowed_packet = 16M
max_binlog_size = 100M
max_connections = 4096
open_files_limit = 65536
pid-file = /var/lib/mysql/mariadb.pid
plugin_load_add = auth_ed25519
port = 3306
query_cache_size = 0
query_cache_type = 0
skip-external-locking
socket = /var/lib/mysql/mysql.sock
ssl = false
thread_cache_size = 8
thread_stack = 256K
tmpdir = /tmp
user = mysql
[mysqld-5.0]
myisam-recover = BACKUP
[mysqld-5.1]
myisam-recover = BACKUP
[mysqld-5.5]
myisam-recover = BACKUP
query_cache_limit = 1M
query_cache_size = 16M
[mysqld-5.6]
myisam-recover-options = BACKUP
query_cache_limit = 1M
query_cache_size = 16M
[mysqld-5.7]
myisam-recover-options = BACKUP
query_cache_limit = 1M
query_cache_size = 16M
[mysqld_safe]
log-error = /var/log/mariadb/mariadb.log
nice = 0
pid-file = /var/lib/mysql/mariadb.pid
socket = /var/lib/mysql/mysql.sock
[mysqldump]
max_allowed_packet = 16M
quick
quote-names

View File

@ -0,0 +1,99 @@
apiVersion: v1
kind: Pod
metadata:
labels:
app: ephemeral-heat-infra
name: ephemeral-heat-infra
spec:
containers:
- name: rabbitmq
command:
- /rabbitmq-init.sh
env:
- name: PATH
value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
- name: TERM
value: xterm
- name: container
value: oci
- name: LANG
value: en_US.UTF-8
image: quay.io/tripleomaster/openstack-rabbitmq:current-tripleo
resources: {}
securityContext:
allowPrivilegeEscalation: true
capabilities:
drop:
- CAP_MKNOD
- CAP_AUDIT_WRITE
privileged: false
readOnlyRootFilesystem: false
runAsGroup: 42439
runAsUser: 42439
seLinuxOptions: {}
volumeMounts:
- mountPath: /rabbitmq-init.sh
name: rabbitmq-init
- mountPath: /rabbitmq.config
name: rabbitmq-config
workingDir: /
- name: mysql
command:
- /mysql-init.sh
args: []
env:
- name: PATH
value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
- name: TERM
value: xterm
- name: container
value: oci
- name: LANG
value: en_US.UTF-8
- name: DB_ROOT_PASSWORD
value: {{ db_password }}
- name: DB_MAX_TIMEOUT
value: {{ db_timeout }}
image: quay.io/tripleomaster/openstack-mariadb:current-tripleo
resources: {}
securityContext:
allowPrivilegeEscalation: true
capabilities:
drop:
- CAP_MKNOD
- CAP_AUDIT_WRITE
privileged: false
readOnlyRootFilesystem: false
runAsGroup: 0
runAsUser: 0
seLinuxOptions: {}
volumeMounts:
- mountPath: /etc/my.cnf.d/galera.cnf
name: mysql-config
- mountPath: /mysql-init.sh
name: mysql-init
- mountPath: /root/.my.cnf
name: my-cnf
workingDir: /
volumes:
- hostPath:
path: {{ heat_dir }}/galera.cnf
type: File
name: mysql-config
- hostPath:
path: {{ heat_dir }}/mysql-init.sh
type: File
name: mysql-init
- hostPath:
path: {{ heat_dir }}/my.cnf
type: File
name: my-cnf
- hostPath:
path: {{ heat_dir }}/rabbitmq.config
type: File
name: rabbitmq-config
- hostPath:
path: {{ heat_dir }}/rabbitmq-init.sh
type: File
name: rabbitmq-init
status: {}

View File

@ -0,0 +1,5 @@
[mysql]
user=root
host=localhost
password={{ db_password }}
socket=/var/lib/mysql/mysql.sock

View File

@ -0,0 +1,16 @@
#!/bin/bash
set -eux
if [ ! -d /var/lib/mysql/mysql ]; then
mysql_install_db
fi
DATABASE_IP=$(ip -f inet addr show eth0 | awk '/inet / {print $2}' | cut -d/ -f1)
sed -e "s/DATABASE_IP/$DATABASE_IP/" /etc/my.cnf.d/galera.cnf > /tmp/galera.cnf
cat /tmp/galera.cnf > /etc/my.cnf.d/galera.cnf
mysqld_safe --wsrep-on=OFF &
timeout ${DB_MAX_TIMEOUT}s /bin/bash -c 'until mysqladmin -uroot ping 2>/dev/null; do sleep 1; done'
mysqladmin -u root password $DB_ROOT_PASSWORD
mysql -uroot -p"${DB_ROOT_PASSWORD}" -e "CREATE USER 'mysql'@'localhost';"
mysql -uroot -p"${DB_ROOT_PASSWORD}" -e "REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'mysql'@'localhost';"
sleep infinity

View File

@ -0,0 +1,8 @@
#!/bin/bash
set -eux
RABBITMQ_IP=$(ip -f inet addr show eth0 | awk '/inet / {print $2}' | cut -d/ -f1)
sed -e "s/RABBITMQ_IP/$RABBITMQ_IP/" /rabbitmq.config > /etc/rabbitmq/rabbitmq.config
/usr/lib/rabbitmq/bin/rabbitmq-server &
sleep infinity

View File

@ -0,0 +1,31 @@
[
{rabbit, [
{loopback_users, []},
{tcp_listen_options, [
{keepalive, true},
{backlog, 4096},
{nodelay, true},
{linger, {true, 0}},
{exit_on_close, false}
]},
{collect_statistics_interval, 30000},
{tcp_listeners, [{"RABBITMQ_IP", 5672}]},
{cluster_partition_handling, ignore},
{queue_master_locator, <<"min-masters">>},
{default_user, <<"guest">>},
{default_pass, <<"guest">>}
]},
{kernel, [
{inet_dist_listen_max, 25672},
{inet_dist_listen_min, 25672},
{net_ticktime, 15}
]}
,
{rabbitmq_management, [
{rates_mode, none}
, {listener, [
{ip, "127.0.0.1"},
{port, 15672}
]}
]}
].

View File

@ -37,6 +37,7 @@ STANDALONE_NETWORKS_FILE = "/dev/null"
UNDERCLOUD_NETWORKS_FILE = "network_data_undercloud.yaml"
ANSIBLE_HOSTS_FILENAME = "hosts.yaml"
EPHEMERAL_HEAT_POD_NAME = "ephemeral-heat"
EPHEMERAL_HEAT_INFRA_POD_NAME = "ephemeral-heat-infra"
ANSIBLE_CWL = "tripleo_dense,tripleo_profile_tasks,tripleo_states"
CONTAINER_IMAGE_PREPARE_LOG_FILE = "container_image_prepare.log"
DEFAULT_CONTAINER_REGISTRY = "quay.io"

View File

@ -143,3 +143,7 @@ class HeatPodMessageQueueException(Base):
class InvalidPlaybook(Base):
"""Invalid playbook path specified"""
class HeatPodDatabaseException(Base):
"""Heat database does not seem to be available"""

View File

@ -41,8 +41,10 @@ from tripleoclient.constants import (DEFAULT_HEAT_CONTAINER,
DEFAULT_EPHEMERAL_HEAT_API_CONTAINER,
DEFAULT_EPHEMERAL_HEAT_ENGINE_CONTAINER,
DEFAULT_TEMPLATES_DIR,
EPHEMERAL_HEAT_INFRA_POD_NAME,
EPHEMERAL_HEAT_POD_NAME)
from tripleoclient.exceptions import HeatPodMessageQueueException
from tripleoclient.exceptions import (HeatPodMessageQueueException,
HeatPodDatabaseException)
log = logging.getLogger(__name__)
@ -198,7 +200,8 @@ class HeatBaseLauncher(object):
self.user = user
self._write_fake_keystone_token(self.api_port, self.token_file)
self._write_heat_config()
if getattr(self, 'do_write_heat_config', True):
self._write_heat_config()
self._write_api_paste_config()
if use_root:
uid = int(self.get_heat_uid())
@ -455,6 +458,9 @@ class HeatNativeLauncher(HeatBaseLauncher):
class HeatPodLauncher(HeatContainerLauncher):
heat_type = 'pod'
mysql_container = 'mysql'
rabbitmq_container = 'rabbitmq'
db_password = ''
def __init__(self, *args, **kwargs):
super(HeatPodLauncher, self).__init__(*args, **kwargs)
@ -494,10 +500,19 @@ class HeatPodLauncher(HeatContainerLauncher):
raise Exception('Unable to fetch container image {}.'
'Error: {}'.format(image, e))
def get_pod_state(self):
def get_container_state(self, container='ephemeral-heat-api'):
inspect = subprocess.run([
'sudo', 'podman', 'inspect', '--format',
'{{.State.Status}}', container],
check=False,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
return self._decode(inspect.stdout)
def get_pod_state(self, pod='ephemeral-heat'):
inspect = subprocess.run([
'sudo', 'podman', 'pod', 'inspect', '--format',
'"{{.State}}"', EPHEMERAL_HEAT_POD_NAME],
'"{{.State}}"', pod],
check=False,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
@ -518,22 +533,22 @@ class HeatPodLauncher(HeatContainerLauncher):
if not self.database_exists():
subprocess.check_call([
'sudo', 'podman', 'exec', '-u', 'root',
'mysql', 'mysql', '-e', 'create database heat'
self.mysql_container, 'mysql', '-e', 'create database heat'
])
subprocess.check_call([
'sudo', 'podman', 'exec', '-u', 'root',
'mysql', 'mysql', '-e',
self.mysql_container, 'mysql', '-e',
'create user if not exists '
'\'heat\'@\'%\' identified by \'heat\''
])
subprocess.check_call([
'sudo', 'podman', 'exec', '-u', 'root',
'mysql', 'mysql', 'heat', '-e',
self.mysql_container, 'mysql', 'heat', '-e',
'grant all privileges on heat.* to \'heat\'@\'%\''
])
subprocess.check_call([
'sudo', 'podman', 'exec', '-u', 'root',
'mysql', 'mysql', '-e', 'flush privileges;'
self.mysql_container, 'mysql', '-e', 'flush privileges;'
])
cmd = [
'sudo', 'podman', 'run', '--rm',
@ -580,17 +595,20 @@ class HeatPodLauncher(HeatContainerLauncher):
"Remove it first." % db_dump_path)
log.info("Starting back up of heat db")
with open(db_dump_path, 'w') as out:
subprocess.run([
backup_command = [
'sudo', 'podman', 'exec', '-u', 'root',
'mysql', 'mysqldump', 'heat'], stdout=out,
check=True)
self.mysql_container, 'mysqldump', '-uroot']
if self.db_password:
backup_command.extend(['-p{}'.format(self.db_password)])
backup_command.extend(['heat'])
subprocess.run(backup_command, stdout=out, check=True)
self.tar_file(db_dump_path)
def pod_exists(self):
def pod_exists(self, pod_name='ephemeral-heat'):
try:
subprocess.check_call(
['sudo', 'podman', 'pod', 'inspect', EPHEMERAL_HEAT_POD_NAME],
['sudo', 'podman', 'pod', 'inspect', pod_name],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
return True
@ -633,53 +651,69 @@ class HeatPodLauncher(HeatContainerLauncher):
])
log.info("Stopped pod: %s", EPHEMERAL_HEAT_POD_NAME)
@retry(retry=retry_if_exception_type(HeatPodMessageQueueException),
reraise=True,
stop=(stop_after_delay(10) | stop_after_attempt(10)),
wait=wait_fixed(0.5))
def check_message_bus(self):
log.info("Checking that message bus (rabbitmq) is up")
log.info("Checking that message bus (rabbitmq) is available...")
try:
subprocess.check_call([
'sudo', 'podman', 'exec', '-u', 'root', 'rabbitmq',
'sudo', 'podman', 'exec', '-u', 'root',
self.rabbitmq_container,
'rabbitmqctl', 'list_queues'],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
log.info("Message bus (rabbitmq) is available")
return True
except subprocess.CalledProcessError as cpe:
log.error("The message bus (rabbitmq) does not seem "
"to be available")
log.error(cpe)
raise
except subprocess.CalledProcessError:
msg = "The message bus (rabbitmq) is not available"
raise HeatPodMessageQueueException(msg)
def _get_database_ip(self):
return self._get_database_ip()
@retry(retry=retry_if_exception_type(HeatPodDatabaseException),
reraise=True,
stop=(stop_after_delay(10) | stop_after_attempt(10)),
wait=wait_fixed(0.5))
def check_database(self):
log.info("Checking that database is up")
log.info("Checking that database is available...")
try:
subprocess.check_call([
'sudo', 'podman', 'exec', '-u', 'root', 'mysql',
'mysql', '-h', self._get_ctlplane_ip(), '-e',
'sudo', 'podman', 'exec', '-u', 'root', self.mysql_container,
'mysql', '-e',
'show databases;'],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
log.info("Database available.")
return True
except subprocess.CalledProcessError as cpe:
log.error("The database does not seem to be available")
log.error(cpe)
raise
except subprocess.CalledProcessError:
msg = "The database is not available"
raise HeatPodDatabaseException(msg)
def database_exists(self):
if self.get_container_state(self.mysql_container).lower() != "running":
return False
output = subprocess.check_output([
'sudo', 'podman', 'exec', '-u', 'root', 'mysql',
'sudo', 'podman', 'exec', '-u', 'root', self.mysql_container,
'mysql', '-e', 'show databases like "heat"'
])
return 'heat' in str(output)
def kill_heat(self, pid):
if self.pod_exists():
log.info("Killing pod: %s", EPHEMERAL_HEAT_POD_NAME)
def kill_pod(self, pod_name=EPHEMERAL_HEAT_POD_NAME):
if self.pod_exists(pod_name):
log.info("Killing pod: {}".format(pod_name))
subprocess.call([
'sudo', 'podman', 'pod', 'kill',
EPHEMERAL_HEAT_POD_NAME
'sudo', 'podman', 'pod', 'kill', pod_name
])
log.info("Killed pod: %s", EPHEMERAL_HEAT_POD_NAME)
log.info("Killed pod: {}".format(pod_name))
else:
log.info("Pod does not exist: %s", EPHEMERAL_HEAT_POD_NAME)
log.info("Pod does not exist: {}".format(pod_name))
def kill_heat(self, pid):
self.kill_pod()
def _decode(self, encoded):
if not encoded:
@ -687,7 +721,7 @@ class HeatPodLauncher(HeatContainerLauncher):
decoded = encoded.decode('utf-8')
if decoded.endswith('\n'):
decoded = decoded[:-1]
return decoded
return decoded.strip('"')
def _get_transport_url(self):
user = self._decode(subprocess.check_output(
@ -727,7 +761,7 @@ class HeatPodLauncher(HeatContainerLauncher):
def wait_for_message_queue(self):
queue_name = 'engine.' + EPHEMERAL_HEAT_POD_NAME
output = subprocess.check_output([
'sudo', 'podman', 'exec', 'rabbitmq',
'sudo', 'podman', 'exec', self.rabbitmq_container,
'rabbitmqctl', 'list_queues'])
if str(output).count(queue_name) < 1:
msg = "Message queue for ephemeral heat not created in time."
@ -742,42 +776,156 @@ class HeatPodLauncher(HeatContainerLauncher):
return config
def _write_heat_config(self):
heat_config_tmpl_path = os.path.join(DEFAULT_TEMPLATES_DIR,
EPHEMERAL_HEAT_POD_NAME,
"heat.conf.j2")
with open(heat_config_tmpl_path) as tmpl:
heat_config_tmpl = jinja2.Template(tmpl.read())
self.render_template(
'heat.conf.j2', 'heat.conf',
transport_url=self._get_transport_url(),
db_connection=self._get_db_connection(),
api_port=self.api_port,
num_engine_workers=self._get_num_engine_workers(),
log_file=self.log_file)
config_vars = {
"transport_url": self._get_transport_url(),
"db_connection": self._get_db_connection(),
"api_port": self.api_port,
"num_engine_workers": self._get_num_engine_workers(),
"log_file": self.log_file,
def _write_heat_pod(self, template='heat-pod.yaml.j2',
rendered='heat-pod.yaml', **template_vars):
if not template_vars:
pod_vars = {
"install_dir": self.install_dir,
"heat_dir": self.heat_dir,
"ctlplane_ip": self.host,
"api_port": self.api_port,
"api_image": self.api_container_image,
"engine_image": self.engine_container_image,
"heat_pod_name": EPHEMERAL_HEAT_POD_NAME
}
else:
pod_vars = template_vars
self.render_template(template, rendered, **pod_vars)
@retry(stop=(stop_after_delay(20) | stop_after_attempt(20)),
wait=wait_fixed(0.5))
def wait_for_container_running(self, container):
container_state = self.get_container_state(container)
if container_state.lower() != 'running':
msg = ("Container {} not running. State is {}".format(
container, container_state))
log.warning(msg)
raise Exception(msg)
@retry(stop=(stop_after_delay(20) | stop_after_attempt(20)),
wait=wait_fixed(0.5))
def wait_for_pod_running(self, pod):
pod_state = self.get_pod_state(pod)
if pod_state.lower() != 'running':
msg = ("Pod {} not running. State is {}".format(
pod, pod_state))
log.warning(msg)
raise Exception(msg)
def render_template(self, templ_name, final_name, **templ_vars):
templ_path = os.path.join(
DEFAULT_TEMPLATES_DIR, EPHEMERAL_HEAT_POD_NAME,
templ_name)
with open(templ_path) as tmpl:
templ = jinja2.Template(tmpl.read())
rendered = templ.render(**templ_vars)
with open(os.path.join(self.heat_dir, final_name), 'w') as out:
out.write(rendered)
class HeatFullLauncher(HeatPodLauncher):
heat_type = 'full'
mysql_container = 'ephemeral-heat-infra-mysql'
rabbitmq_container = 'ephemeral-heat-infra-rabbitmq'
db_password = 'root'
def __init__(self, *args, **kwargs):
self.do_write_heat_config = False
super(HeatFullLauncher, self).__init__(*args, **kwargs)
self.host = "127.0.0.1"
def launch_heat_infra(self):
if "Running" in self.get_pod_state(EPHEMERAL_HEAT_INFRA_POD_NAME):
log.info("%s pod already running, skipping launch",
EPHEMERAL_HEAT_INFRA_POD_NAME)
return
infra_vars = {
'db_password': self.db_password,
'db_timeout': 60,
'heat_dir': self.heat_dir,
}
heat_config = heat_config_tmpl.render(**config_vars)
self._write_heat_pod(
'heat-infra-pod.yaml.j2', 'heat-infra-pod.yaml', **infra_vars)
with open(self.config_file, 'w') as conf:
conf.write(heat_config)
mysql_config_path = os.path.join(
DEFAULT_TEMPLATES_DIR, "ephemeral-heat",
"galera.cnf")
mysql_init_path = os.path.join(
DEFAULT_TEMPLATES_DIR, "ephemeral-heat",
"mysql-init.sh")
shutil.copy(mysql_config_path, self.heat_dir)
shutil.copy(mysql_init_path, self.heat_dir)
def _write_heat_pod(self):
heat_pod_tmpl_path = os.path.join(DEFAULT_TEMPLATES_DIR,
EPHEMERAL_HEAT_POD_NAME,
"heat-pod.yaml.j2")
with open(heat_pod_tmpl_path) as tmpl:
heat_pod_tmpl = jinja2.Template(tmpl.read())
rabbitmq_init_path = os.path.join(
DEFAULT_TEMPLATES_DIR, "ephemeral-heat",
"rabbitmq-init.sh")
rabbitmq_config_path = os.path.join(
DEFAULT_TEMPLATES_DIR, "ephemeral-heat",
"rabbitmq.config")
shutil.copy(rabbitmq_init_path, self.heat_dir)
shutil.copy(rabbitmq_config_path, self.heat_dir)
pod_vars = {
"install_dir": self.install_dir,
"heat_dir": self.heat_dir,
"ctlplane_ip": self.host,
"api_port": self.api_port,
"api_image": self.api_container_image,
"engine_image": self.engine_container_image,
"heat_pod_name": EPHEMERAL_HEAT_POD_NAME
}
heat_pod = heat_pod_tmpl.render(**pod_vars)
self.render_template("my.cnf.j2", "my.cnf",
db_password=self.db_password)
heat_pod_path = os.path.join(self.heat_dir, "heat-pod.yaml")
with open(heat_pod_path, 'w') as conf:
conf.write(heat_pod)
subprocess.check_call([
'sudo', 'podman', 'play', 'kube',
os.path.join(self.heat_dir, 'heat-infra-pod.yaml')
])
self.wait_for_pod_running(EPHEMERAL_HEAT_INFRA_POD_NAME)
self.wait_for_container_running("ephemeral-heat-infra-rabbitmq")
self.wait_for_container_running("ephemeral-heat-infra-mysql")
def _get_infra_ip(self):
ip = self._decode(subprocess.check_output(
['sudo', 'podman', 'inspect', '--format',
'{{.NetworkSettings.IPAddress}}',
'ephemeral-heat-infra-rabbitmq']))
return ip
def _get_database_ip(self):
return self._get_infra_ip()
def _get_transport_url(self):
transport_url = "rabbit://%s:%s@%s:%s/?ssl=0" % \
('guest', 'guest',
self._get_infra_ip(), 5672)
return transport_url
def _get_db_connection(self):
return ('mysql+pymysql://'
'heat:heat@{}/heat?read_default_file='
'/etc/my.cnf.d/tripleo.cnf&read_default_group=tripleo'.format(
self._get_infra_ip()))
def kill_heat(self, pid):
self.kill_pod(EPHEMERAL_HEAT_POD_NAME)
def rm_heat(self):
super(HeatFullLauncher, self).rm_heat()
self.kill_pod(EPHEMERAL_HEAT_INFRA_POD_NAME)
if self.pod_exists(EPHEMERAL_HEAT_INFRA_POD_NAME):
log.info("Removing pod: %s", EPHEMERAL_HEAT_INFRA_POD_NAME)
subprocess.call([
'sudo', 'podman', 'pod', 'rm', '-f',
EPHEMERAL_HEAT_INFRA_POD_NAME
])
def launch_heat(self):
self.launch_heat_infra()
self._write_heat_config()
self.check_database()
self.heat_db_sync()
super(HeatFullLauncher, self).launch_heat()

View File

@ -22,7 +22,8 @@ from unittest import mock
from tripleoclient import constants
from tripleoclient import heat_launcher
from tripleoclient.exceptions import HeatPodMessageQueueException
from tripleoclient.exceptions import (HeatPodDatabaseException,
HeatPodMessageQueueException)
from tripleoclient.tests import base
from tripleoclient import utils
@ -264,7 +265,7 @@ class TestHeatPodLauncher(base.TestCase):
mock_tar.assert_called_with(str(p))
self.run.assert_called_once_with(['sudo', 'podman', 'exec', '-u',
'root', 'mysql', 'mysqldump',
'heat'],
'-uroot', 'heat'],
check=True, stdout=mock.ANY)
def test_pod_exists(self):
@ -378,6 +379,7 @@ class TestHeatPodLauncher(base.TestCase):
def test_check_message_bus(self):
launcher = self.get_launcher()
self.check_call.reset_mock()
check_mb = launcher.check_message_bus.__wrapped__
launcher.check_message_bus()
self.check_call.assert_called_once_with(['sudo', 'podman', 'exec',
'-u', 'root', 'rabbitmq',
@ -387,35 +389,34 @@ class TestHeatPodLauncher(base.TestCase):
self.check_call.reset_mock()
self.check_call.side_effect = subprocess.CalledProcessError(1, 'test')
self.assertRaises(subprocess.CalledProcessError,
launcher.check_message_bus)
self.assertRaises(HeatPodMessageQueueException, check_mb, launcher)
@mock.patch(
'tripleoclient.heat_launcher.HeatPodLauncher._get_ctlplane_ip')
def test_check_database(self, mock_ctlplane_ip):
launcher = self.get_launcher()
self.check_call.reset_mock()
check_db = launcher.check_database.__wrapped__
mock_ctlplane_ip.return_value = '1.1.1.1'
self.assertTrue(launcher.check_database())
mock_ctlplane_ip.assert_called()
self.check_call.assert_called_once_with(['sudo', 'podman', 'exec',
'-u', 'root', 'mysql',
'mysql', '-h', '1.1.1.1',
'mysql',
'-e', 'show databases;'],
stderr=subprocess.DEVNULL,
stdout=subprocess.DEVNULL)
self.check_call.reset_mock()
mock_ctlplane_ip.reset_mock()
self.check_call.side_effect = subprocess.CalledProcessError(1, '/test')
self.assertRaises(subprocess.CalledProcessError,
launcher.check_database)
self.assertRaises(HeatPodDatabaseException, check_db, launcher)
def test_database_exists(self):
launcher = self.get_launcher()
self.check_output.reset_mock()
self.check_output.return_value = 'heat'
mock_running = mock.Mock()
mock_running.stdout = 'Running'.encode()
self.run.return_value = mock_running
self.assertTrue(launcher.database_exists())
self.check_output.assert_called_once_with([
'sudo', 'podman', 'exec', '-u', 'root', 'mysql', 'mysql', '-e',

View File

@ -2755,10 +2755,12 @@ def launch_heat(launcher=None, restore_db=False, heat_type='pod'):
_heat_pid = 0
if launcher.heat_type == 'native':
_heat_pid = os.fork()
if _heat_pid == 0:
if _heat_pid == 0 and launcher.heat_type != 'full':
launcher.check_database()
launcher.check_message_bus()
launcher.heat_db_sync(restore_db)
if _heat_pid == 0:
launcher.launch_heat()
# Wait for the API to be listening
@ -2783,6 +2785,9 @@ def get_heat_launcher(heat_type, *args, **kwargs):
return heat_launcher.HeatNativeLauncher(*args, **kwargs)
if heat_type == 'container':
return heat_launcher.HeatContainerLauncher(*args, **kwargs)
if heat_type == 'full':
return heat_launcher.HeatFullLauncher(*args, **kwargs)
return heat_launcher.HeatPodLauncher(*args, **kwargs)

View File

@ -973,6 +973,8 @@ class DeployOvercloud(command.Command):
help=_('The type of Heat process to use to execute '
'the deployment.\n'
'pod (Default): Use an ephemeral Heat pod.\n'
'full: Use a full ephemeral Heat pod, including rabbitmq '
'and mariadb.\n'
'installed: Use the system installed Heat.\n'
'container: Use an ephemeral Heat container.\n'
'native: Use an ephemeral Heat process.')

View File

@ -56,7 +56,6 @@ class LaunchHeat(command.Command):
return 0
def _launch_heat(self, parsed_args):
self.log.info("Launching Heat %s" % parsed_args.heat_type)
utils.launch_heat(self.heat_launcher, parsed_args.restore_db)
return 0
@ -135,7 +134,7 @@ class LaunchHeat(command.Command):
'--rm-heat',
action='store_true',
default=False,
help=_('If specified and --heat-type is container or pod '
help=_('If specified and --heat-type is container, pod, or full '
'any existing container or pod of a previous '
'ephemeral Heat process will be deleted first. '
'Ignored if --heat-type is native or --kill.')
@ -144,7 +143,7 @@ class LaunchHeat(command.Command):
'--skip-heat-pull',
action='store_true',
default=False,
help=_('When --heat-type is pod or container, assume '
help=_('When --heat-type is container, pod, or full assume '
'the container image has already been pulled ')
)
parser.add_argument(
@ -173,12 +172,16 @@ class LaunchHeat(command.Command):
'--heat-type',
dest='heat_type',
default='native',
choices=['native', 'container', 'pod'],
choices=['native', 'container', 'pod', 'full'],
help=_('Type of ephemeral Heat process to launch. One of:\n'
'native: Execute heat-all directly on the host.\n'
'container: Execute heat-all in a container.\n'
'pod: Execute separate heat api and engine processes in '
'a podman pod.')
'pod: Execute separate heat api and engine processes in\n'
' a podman pod.\n'
'full: Execute separate heat api and engine processes in\n'
' a pod, and also start an infra pod for the '
' database\n'
' and message bus.')
)
return parser
@ -196,6 +199,7 @@ class LaunchHeat(command.Command):
else:
rm_heat = parsed_args.rm_heat
self.log.info("Launching Heat %s" % parsed_args.heat_type)
self.heat_launcher = utils.get_heat_launcher(
heat_type, parsed_args.heat_api_port,
parsed_args.heat_container_image,