233 lines
7.0 KiB
Python
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)
|