Merge "Add OS::TripleO::UndercloudUpgradeEphemeralHeat"
This commit is contained in:
commit
1ba30719ee
|
@ -0,0 +1,54 @@
|
||||||
|
heat_template_version: wallaby
|
||||||
|
|
||||||
|
description: >
|
||||||
|
Upgrade an undercloud to use ephemeral Heat
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
ServiceData:
|
||||||
|
default: {}
|
||||||
|
description: Dictionary packing service data
|
||||||
|
type: json
|
||||||
|
ServiceNetMap:
|
||||||
|
default: {}
|
||||||
|
description: Mapping of service_name -> network name. Typically set
|
||||||
|
via parameter_defaults in the resource registry. This
|
||||||
|
mapping overrides those in ServiceNetMapDefaults.
|
||||||
|
type: json
|
||||||
|
RoleName:
|
||||||
|
default: ''
|
||||||
|
description: Role name on which the service is applied
|
||||||
|
type: string
|
||||||
|
RoleParameters:
|
||||||
|
default: {}
|
||||||
|
description: Parameters specific to the role
|
||||||
|
type: json
|
||||||
|
EndpointMap:
|
||||||
|
default: {}
|
||||||
|
description: Mapping of service endpoint -> protocol. Typically set
|
||||||
|
via parameter_defaults in the resource registry.
|
||||||
|
type: json
|
||||||
|
|
||||||
|
outputs:
|
||||||
|
role_data:
|
||||||
|
description: Role data for the TripleO Undercloud Upgrade Ephemeral Heat service.
|
||||||
|
value:
|
||||||
|
service_name: undercloud_upgrade_ephemeral_heat
|
||||||
|
upgrade_tasks:
|
||||||
|
- name: Create /var/lib/tripleo-config/scripts dir
|
||||||
|
file:
|
||||||
|
path: /var/lib/tripleo-config/scripts
|
||||||
|
state: directory
|
||||||
|
recurse: true
|
||||||
|
when:
|
||||||
|
- step|int == 1
|
||||||
|
- name: Copy undercloud-upgrade-ephemeral-heat.py to /var/lib/tripleo-config/scripts
|
||||||
|
copy:
|
||||||
|
dest: /var/lib/tripleo-config/scripts/undercloud-upgrade-ephemeral-heat.py
|
||||||
|
content: {get_file: ../../scripts/undercloud-upgrade-ephemeral-heat.py}
|
||||||
|
mode: 0755
|
||||||
|
when:
|
||||||
|
- step|int == 1
|
||||||
|
- name: Run undercloud-upgrade-ephemeral-heat.py
|
||||||
|
shell: /var/lib/tripleo-config/scripts/undercloud-upgrade-ephemeral-heat.py
|
||||||
|
when:
|
||||||
|
- step|int == 1
|
|
@ -1,8 +1,8 @@
|
||||||
# A Heat environment file that can be used to upgrade a non-containerized undercloud
|
# A Heat environment file that can be used to upgrade an undercloud
|
||||||
# to a containerized undercloud.
|
|
||||||
|
|
||||||
resource_registry:
|
resource_registry:
|
||||||
OS::TripleO::Services::UndercloudUpgrade: ../../deployment/undercloud/undercloud-upgrade.yaml
|
OS::TripleO::Services::UndercloudUpgrade: ../../deployment/undercloud/undercloud-upgrade.yaml
|
||||||
|
OS::TripleO::Services::UndercloudUpgradeEphemeralHeat: ../../deployment/undercloud/undercloud-upgrade-ephemeral-heat.yaml
|
||||||
|
|
||||||
parameter_defaults:
|
parameter_defaults:
|
||||||
UndercloudUpgrade: true
|
UndercloudUpgrade: true
|
||||||
|
|
|
@ -237,6 +237,7 @@ resource_registry:
|
||||||
OS::TripleO::Services::MasqueradeNetworks: OS::Heat::None
|
OS::TripleO::Services::MasqueradeNetworks: OS::Heat::None
|
||||||
OS::TripleO::Services::TripleoValidations: OS::Heat::None
|
OS::TripleO::Services::TripleoValidations: OS::Heat::None
|
||||||
OS::TripleO::Services::UndercloudUpgrade: OS::Heat::None
|
OS::TripleO::Services::UndercloudUpgrade: OS::Heat::None
|
||||||
|
OS::TripleO::Services::UndercloudUpgradeEphemeralHeat: OS::Heat::None
|
||||||
OS::TripleO::Services::Collectd: OS::Heat::None
|
OS::TripleO::Services::Collectd: OS::Heat::None
|
||||||
OS::TripleO::Services::ManilaApi: OS::Heat::None
|
OS::TripleO::Services::ManilaApi: OS::Heat::None
|
||||||
OS::TripleO::Services::ManilaScheduler: OS::Heat::None
|
OS::TripleO::Services::ManilaScheduler: OS::Heat::None
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
features:
|
||||||
|
- A new service, OS::TripleO::Services::UndercloudUpgradeEphemeralHeat is
|
||||||
|
added to the Undercloud role. The service is mapped to OS::Heat::None by
|
||||||
|
default, but when environments/lifecycle/undercloud-upgrade-prepare.yaml is
|
||||||
|
included, the service will be enabled and will migrate any already deployed
|
||||||
|
stacks in the undercloud's Heat instance to be able to be used with the
|
||||||
|
ephemeral Heat deployment option from tripleoclient.
|
|
@ -98,5 +98,6 @@
|
||||||
- OS::TripleO::Services::TripleoFirewall
|
- OS::TripleO::Services::TripleoFirewall
|
||||||
- OS::TripleO::Services::Tuned
|
- OS::TripleO::Services::Tuned
|
||||||
- OS::TripleO::Services::UndercloudUpgrade
|
- OS::TripleO::Services::UndercloudUpgrade
|
||||||
|
- OS::TripleO::Services::UndercloudUpgradeEphemeralHeat
|
||||||
- OS::TripleO::Services::TripleoValidations
|
- OS::TripleO::Services::TripleoValidations
|
||||||
- OS::TripleO::Services::Zaqar
|
- OS::TripleO::Services::Zaqar
|
||||||
|
|
|
@ -101,5 +101,6 @@
|
||||||
- OS::TripleO::Services::TripleoFirewall
|
- OS::TripleO::Services::TripleoFirewall
|
||||||
- OS::TripleO::Services::Tuned
|
- OS::TripleO::Services::Tuned
|
||||||
- OS::TripleO::Services::UndercloudUpgrade
|
- OS::TripleO::Services::UndercloudUpgrade
|
||||||
|
- OS::TripleO::Services::UndercloudUpgradeEphemeralHeat
|
||||||
- OS::TripleO::Services::TripleoValidations
|
- OS::TripleO::Services::TripleoValidations
|
||||||
- OS::TripleO::Services::Zaqar
|
- OS::TripleO::Services::Zaqar
|
||||||
|
|
|
@ -0,0 +1,256 @@
|
||||||
|
#!/usr/libexec/platform-python
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""Migrate an undercloud's stack data to use ephemeral Heat. Queries for
|
||||||
|
existing stacks and exports necessary data from the stack to the default
|
||||||
|
consistent working directory before backing up and dropping the heat database.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import tarfile
|
||||||
|
import time
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
from heatclient.client import Client
|
||||||
|
import keystoneauth1
|
||||||
|
import openstack
|
||||||
|
from tripleo_common.utils import plan as plan_utils
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger('undercloud')
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Upgrade an undercloud for ephemeral Heat.")
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--cloud', '-c',
|
||||||
|
default='undercloud',
|
||||||
|
help='The name of the cloud used for the OpenStack connection.')
|
||||||
|
parser.add_argument(
|
||||||
|
'--stack', '-s',
|
||||||
|
action='append',
|
||||||
|
help='The stack(s) to migrate to using ephemeral Heat. Can be '
|
||||||
|
'specified multiple times. If not specified, all stacks '
|
||||||
|
'will be migrated')
|
||||||
|
parser.add_argument(
|
||||||
|
'--working-dir', '-w',
|
||||||
|
help='Directory to use for saving stack state. '
|
||||||
|
'Defaults to ~/overcloud-deploy/<stack>')
|
||||||
|
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
def database_exists():
|
||||||
|
"""Check if the heat database exists.
|
||||||
|
|
||||||
|
:return: True if the heat database exists, otherwise False
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
output = subprocess.check_output([
|
||||||
|
'sudo', 'podman', 'exec', '-u', 'root', 'mysql',
|
||||||
|
'mysql', '-e', 'show databases like "heat"'
|
||||||
|
])
|
||||||
|
return 'heat' in str(output)
|
||||||
|
|
||||||
|
|
||||||
|
def backup_db(backup_dir):
|
||||||
|
"""Backup the heat database to the specified directory
|
||||||
|
|
||||||
|
:param backup_dir: The directory to store the backup
|
||||||
|
:type backup_dir: str
|
||||||
|
:return: Database tarfile backup path
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
heat_dir = os.path.join(backup_dir, 'heat-launcher')
|
||||||
|
if not os.path.isdir(heat_dir):
|
||||||
|
os.makedirs(heat_dir)
|
||||||
|
db_path = os.path.join(heat_dir, 'heat-db.sql')
|
||||||
|
LOG.info("Backing up heat database to {}".format(db_path))
|
||||||
|
with open(db_path, 'w') as out:
|
||||||
|
subprocess.run([
|
||||||
|
'sudo', 'podman', 'exec', '-u', 'root',
|
||||||
|
'mysql', 'mysqldump', 'heat'], stdout=out,
|
||||||
|
check=True)
|
||||||
|
os.chmod(db_path, 0o600)
|
||||||
|
|
||||||
|
tf_name = '{}-{}.tar.bzip2'.format(db_path, time.time())
|
||||||
|
tf = tarfile.open(tf_name, 'w:bz2')
|
||||||
|
tf.add(db_path, os.path.basename(db_path))
|
||||||
|
tf.close()
|
||||||
|
LOG.info("Created tarfile {}".format(tf_name))
|
||||||
|
|
||||||
|
return tf_name
|
||||||
|
|
||||||
|
|
||||||
|
def _decode(encoded):
|
||||||
|
"""Decode a string into utf-8
|
||||||
|
|
||||||
|
:param encoded: Encoded string
|
||||||
|
:type encoded: string
|
||||||
|
:return: Decoded string
|
||||||
|
:rtype: string
|
||||||
|
"""
|
||||||
|
if not encoded:
|
||||||
|
return ""
|
||||||
|
decoded = encoded.decode('utf-8')
|
||||||
|
if decoded.endswith('\n'):
|
||||||
|
decoded = decoded[:-1]
|
||||||
|
return decoded
|
||||||
|
|
||||||
|
|
||||||
|
def _get_ctlplane_vip():
|
||||||
|
"""Get the configured ctlplane VIP
|
||||||
|
|
||||||
|
:return: ctlplane VIP
|
||||||
|
:rtype: string
|
||||||
|
"""
|
||||||
|
return _decode(subprocess.check_output(
|
||||||
|
['sudo', 'hiera', 'controller_virtual_ip']))
|
||||||
|
|
||||||
|
|
||||||
|
def _get_ctlplane_ip():
|
||||||
|
"""Get the configured ctlplane IP
|
||||||
|
|
||||||
|
:return: ctlplane IP
|
||||||
|
:rtype: string
|
||||||
|
"""
|
||||||
|
return _decode(subprocess.check_output(
|
||||||
|
['sudo', 'hiera', 'ctlplane']))
|
||||||
|
|
||||||
|
|
||||||
|
def drop_db():
|
||||||
|
"""Drop the heat database and heat users
|
||||||
|
|
||||||
|
:return: None
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
|
LOG.info("Dropping Heat database")
|
||||||
|
subprocess.check_call([
|
||||||
|
'sudo', 'podman', 'exec', '-u', 'root',
|
||||||
|
'mysql', 'mysql', 'heat', '-e',
|
||||||
|
'drop database heat'])
|
||||||
|
LOG.info("Dropping Heat users")
|
||||||
|
subprocess.check_call([
|
||||||
|
'sudo', 'podman', 'exec', '-u', 'root',
|
||||||
|
'mysql', 'mysql', '-e',
|
||||||
|
'drop user \'heat\'@\'{}\''.format(_get_ctlplane_ip())])
|
||||||
|
subprocess.check_call([
|
||||||
|
'sudo', 'podman', 'exec', '-u', 'root',
|
||||||
|
'mysql', 'mysql', '-e',
|
||||||
|
'drop user \'heat\'@\'{}\''.format(_get_ctlplane_vip())])
|
||||||
|
subprocess.check_call([
|
||||||
|
'sudo', 'podman', 'exec', '-u', 'root',
|
||||||
|
'mysql', 'mysql', '-e',
|
||||||
|
'drop user \'heat\'@\'%\''])
|
||||||
|
|
||||||
|
|
||||||
|
def export_passwords(heat, stack, stack_dir):
|
||||||
|
"""Export passwords from an existing stack and write them in Heat
|
||||||
|
environment file format to the specified directory.
|
||||||
|
|
||||||
|
:param cloud: Heat client
|
||||||
|
:type cloud: heatclient.client.Client
|
||||||
|
:param stack: Stack name to query for passwords
|
||||||
|
:type stack: str
|
||||||
|
:param stack_dir: Directory to save the generated Heat environment
|
||||||
|
containing the password values.
|
||||||
|
:type stack_dir: str
|
||||||
|
:return: None
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
|
passwords_path = os.path.join(
|
||||||
|
stack_dir, "tripleo-{}-passwords.yaml".format(stack))
|
||||||
|
LOG.info("Exporting passwords for stack %s to %s"
|
||||||
|
% (stack, passwords_path))
|
||||||
|
passwords = plan_utils.generate_passwords(heat=heat, container=stack)
|
||||||
|
password_params = dict(parameter_defaults=passwords)
|
||||||
|
with open(passwords_path, 'w') as f:
|
||||||
|
f.write(yaml.safe_dump(password_params))
|
||||||
|
os.chmod(passwords_path, 0o600)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
logging.basicConfig()
|
||||||
|
LOG.setLevel(logging.INFO)
|
||||||
|
args = parse_args()
|
||||||
|
|
||||||
|
sudo_user = os.environ.get('SUDO_USER')
|
||||||
|
|
||||||
|
if not args.working_dir:
|
||||||
|
if sudo_user:
|
||||||
|
user_home = '~{}'.format(sudo_user)
|
||||||
|
else:
|
||||||
|
user_home = '~'
|
||||||
|
|
||||||
|
working_dir = os.path.join(
|
||||||
|
os.path.expanduser(user_home),
|
||||||
|
'overcloud-deploy')
|
||||||
|
else:
|
||||||
|
working_dir = args.working_dir
|
||||||
|
if not os.path.isdir(working_dir):
|
||||||
|
os.makedirs(working_dir)
|
||||||
|
|
||||||
|
try:
|
||||||
|
conn = openstack.connection.from_config(cloud=args.cloud)
|
||||||
|
heat = conn.orchestration
|
||||||
|
_heatclient = Client('1', endpoint=conn.endpoint_for('orchestration'),
|
||||||
|
token=conn.auth_token)
|
||||||
|
except keystoneauth1.exceptions.catalog.EndpointNotFound:
|
||||||
|
LOG.warning("No Heat endpoint found, won't migrate any "
|
||||||
|
"existing stack data.")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
stacks = args.stack or [s.name for s in heat.stacks()]
|
||||||
|
except openstack.exceptions.HttpException:
|
||||||
|
LOG.warning("No connection to Heat available, won't migrate any "
|
||||||
|
"existing stack data.")
|
||||||
|
stacks = []
|
||||||
|
|
||||||
|
if database_exists():
|
||||||
|
backup_dir = os.path.join(
|
||||||
|
working_dir,
|
||||||
|
'undercloud-upgrade-ephemeral-heat')
|
||||||
|
db_tar_path = backup_db(backup_dir)
|
||||||
|
else:
|
||||||
|
LOG.warning("No database found to backup.")
|
||||||
|
db_tar_path = None
|
||||||
|
|
||||||
|
for stack in stacks:
|
||||||
|
stack_dir = os.path.join(working_dir, stack)
|
||||||
|
if not os.path.exists(stack_dir):
|
||||||
|
os.makedirs(stack_dir)
|
||||||
|
if db_tar_path:
|
||||||
|
# Symlink to the existing db backup
|
||||||
|
os.symlink(db_tar_path,
|
||||||
|
os.path.join(stack_dir, os.path.basename(db_tar_path)))
|
||||||
|
export_passwords(_heatclient, stack, stack_dir)
|
||||||
|
|
||||||
|
if database_exists():
|
||||||
|
drop_db()
|
||||||
|
|
||||||
|
# Chown all files to original user if running under sudo
|
||||||
|
if sudo_user:
|
||||||
|
subprocess.run([
|
||||||
|
'chown', '-R', '{}:{}'.format(sudo_user, sudo_user),
|
||||||
|
working_dir],
|
||||||
|
check=True)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
Loading…
Reference in New Issue