charm-ironic/hooks/ironic_utils.py

233 lines
7.0 KiB
Python

#
# Copyright 2015 Cloudbase Solutions SRL
#
from charmhelpers.core.hookenv import (
config,
log,
)
from charmhelpers.contrib.openstack import (
templating,
context,
)
from charmhelpers.contrib.openstack.utils import (
os_release,
get_os_codename_install_source,
configure_installation_source,
)
from charmhelpers.core.host import (
service,
service_start,
service_running,
service_reload
)
from ironic_context import (
IronicContext,
NeutronContext,
TFTPContext,
NginxContext,
ObjectStoreContext,
GlanceSwiftBackendContext)
from charmhelpers.fetch import (
apt_update,
apt_upgrade
)
import constants
import os
import subprocess
import urllib
def resource_map():
resource_map = {
constants.IRONIC_CONF: {
'services': ['ironic-api', 'ironic-conductor'],
'contexts': [context.LogLevelContext(),
context.SharedDBContext(
user=config('database-user'),
database=config('database'),
ssl_dir=constants.IRONIC_CONF_DIR),
context.ImageServiceContext(),
context.IdentityServiceContext(
service='ironic',
service_user='ironic'),
NeutronContext(),
context.AMQPContext(
ssl_dir=constants.IRONIC_CONF_DIR),
IronicContext(),
NginxContext(),
ObjectStoreContext(),
GlanceSwiftBackendContext()],
},
constants.TFTP_CONF: {
'services': [constants.TFTP_SERVICE_NAME],
'contexts': [TFTPContext()]
},
constants.HTTPBOOT_AVAILABLE: {
'services': [constants.NGINX_SERVICE_NAME],
'contexts': [NginxContext()]
},
constants.PXELINUX_CONF: {
'services': [],
'contexts': [IronicContext()]
}
}
return resource_map
def register_configs():
'''
Returns an OSTemplateRenderer object with all required configs registered.
'''
release = os_release('ironic-common')
configs = templating.OSConfigRenderer(
templates_dir=constants.TEMPLATES_DIR,
openstack_release=release)
for cfg, d in resource_map().iteritems():
configs.register(cfg, d['contexts'])
return configs
def restart_map():
return {k: v['services'] for k, v in resource_map().iteritems()}
def services():
''' Returns a list of services associated with this charm '''
_services = []
for v in restart_map().values():
_services = _services + v
return list(set(_services))
def cmd_all_services(cmd):
if cmd == 'start':
for svc in services():
if not service_running(svc):
service_start(svc)
else:
for svc in services():
service(cmd, svc)
def determine_ports():
'''Assemble a list of API ports for the services'''
ports = []
for svc in services():
if svc in constants.API_PORTS:
ports.append(constants.API_PORTS[svc])
return list(set(ports))
def migrate_ironic_database():
log('Migrating Ironic database.')
cmd = ['ironic-dbsync',
'--config-file', constants.IRONIC_CONF,
'upgrade']
subprocess.check_output(cmd)
def chown(user=None, file_path=None, recusive=False):
if not user or not file_path:
return
cmd = ['chown', '%s:%s' % (user, user), file_path]
if recusive:
cmd.append('-R')
subprocess.check_output(cmd)
def copy_tftp_files(files_category, tftp_root):
"""This method is used to copy the necessary files expected by Ironic
in the TFTP root directory. The 'constants.TFTP_FILES' directory
is a mapping between the expected files paths (relative to TFTP root
directory) and the source files paths. Depending on the 'files_category'
passed as parameter, the necessary files are copied.
"""
if files_category not in constants.TFTP_FILES:
return
log('Copying TFTP files for category %s' % files_category)
for f_path, src_f_path in constants.TFTP_FILES[files_category].iteritems():
target_f_path = os.path.join(tftp_root, f_path)
urllib.urlretrieve(src_f_path, filename=target_f_path)
def set_up_pxe():
"""Configures the machine to be able to PXE boot Ironic nodes.
"""
ironic_ctxt = IronicContext()()
nginx_ctxt = NginxContext()()
if not os.path.exists(ironic_ctxt['tftp_root']):
os.mkdir(ironic_ctxt['tftp_root'])
# Create directory so we can create the config file later
if not os.path.exists(ironic_ctxt['pxelinux_cfg_root']):
os.mkdir(ironic_ctxt['pxelinux_cfg_root'])
copy_tftp_files('PXE', ironic_ctxt['tftp_root'])
service_reload(constants.TFTP_SERVICE_NAME)
if config('enable-ipxe'):
copy_tftp_files('iPXE', ironic_ctxt['tftp_root'])
if not os.path.exists(nginx_ctxt['http_root']):
os.mkdir(nginx_ctxt['http_root'])
chown(user=constants.IRONIC_USER,
file_path=nginx_ctxt['http_root'],
recusive=True)
if os.path.islink(constants.DEFAULT_SITE_ENABLED):
os.unlink(constants.DEFAULT_SITE_ENABLED)
if not os.path.islink(constants.HTTPBOOT_ENABLED):
os.symlink(constants.HTTPBOOT_AVAILABLE,
constants.HTTPBOOT_ENABLED)
service_reload(constants.NGINX_SERVICE_NAME)
else:
if os.path.islink(constants.HTTPBOOT_ENABLED):
os.unlink(constants.HTTPBOOT_ENABLED)
service_reload(constants.NGINX_SERVICE_NAME)
if config('enable-uefi'):
if not os.path.exists(constants.GRUB_DIR):
os.mkdir(constants.GRUB_DIR)
copy_tftp_files('UEFI', ironic_ctxt['tftp_root'])
chown(user=constants.IRONIC_USER,
file_path=ironic_ctxt['tftp_root'],
recusive=True)
def do_openstack_upgrade(configs):
new_src = config('openstack-origin')
new_os_rel = get_os_codename_install_source(new_src)
log('Performing OpenStack upgrade to %s.' % (new_os_rel))
configure_installation_source(new_src)
dpkg_opts = [
'--option', 'Dpkg::Options::=--force-confnew',
'--option', 'Dpkg::Options::=--force-confdef',
]
apt_update()
apt_upgrade(options=dpkg_opts, fatal=True, dist=True)
# set CONFIGS to load templates from new release
configs.set_release(openstack_release=new_os_rel)
configs.write_all()
def write_all_configs(configs):
if config('enable-ipxe'):
configs.write(constants.HTTPBOOT_AVAILABLE)
configs.write(constants.IRONIC_CONF)
configs.write(constants.TFTP_CONF)
pxelinux_conf_dir = os.path.dirname(constants.PXELINUX_CONF)
if not os.path.exists(pxelinux_conf_dir):
os.makedirs(pxelinux_conf_dir)
configs.write(constants.PXELINUX_CONF)