charm-nova-compute/hooks/nova_compute_utils.py

309 lines
8.6 KiB
Python
Raw Normal View History

import os
import pwd
2013-07-29 12:01:44 -07:00
from base64 import b64decode
from copy import deepcopy
from subprocess import check_call, check_output
2013-07-29 12:01:44 -07:00
2013-07-18 19:37:30 -07:00
from charmhelpers.core.hookenv import (
config,
log,
related_units,
relation_ids,
relation_get,
2013-08-12 14:48:24 -07:00
)
from charmhelpers.contrib.openstack.neutron import neutron_plugin_attribute
2013-07-18 19:37:30 -07:00
from charmhelpers.contrib.openstack.utils import os_release
2013-07-29 18:10:45 -07:00
from charmhelpers.contrib.openstack import templating, context
2013-07-30 20:39:44 -07:00
from nova_compute_context import (
2013-07-29 18:10:45 -07:00
CloudComputeContext,
NovaComputeLibvirtContext,
NovaComputeCephContext,
OSConfigFlagContext,
2013-08-12 14:48:24 -07:00
NeutronComputeContext,
2013-07-29 12:01:44 -07:00
)
2013-07-29 18:10:45 -07:00
CA_CERT_PATH = '/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt'
TEMPLATES = 'templates/'
2013-07-29 18:10:45 -07:00
BASE_PACKAGES = [
'nova-compute',
'genisoimage', # was missing as a package dependency until raring.
]
2013-07-18 19:37:30 -07:00
BASE_RESOURCE_MAP = {
'/etc/libvirt/qemu.conf': {
'services': ['libvirt-bin'],
'contexts': [],
},
'/etc/libvirt/libvirtd.conf': {
'services': ['libvirt-bin'],
'contexts': [NovaComputeLibvirtContext()],
},
'/etc/default/libvirt-bin': {
'services': ['libvirt-bin'],
2013-07-29 18:10:45 -07:00
'contexts': [NovaComputeLibvirtContext()],
},
'/etc/nova/nova.conf': {
'services': ['nova-compute'],
2013-07-29 18:10:45 -07:00
'contexts': [context.AMQPContext(),
context.SharedDBContext(),
context.ImageServiceContext(),
CloudComputeContext(),
NovaComputeCephContext(),
OSConfigFlagContext(),
2013-08-12 14:48:24 -07:00
NeutronComputeContext()]
},
}
2013-07-30 20:39:44 -07:00
CEPH_RESOURCES = {
'/etc/ceph/ceph.conf': {
'contexts': [NovaComputeCephContext()],
'services': [],
},
'/etc/ceph/secret.xml': {
'contexts': [NovaComputeCephContext()],
'services': [],
}
}
2013-07-29 18:10:45 -07:00
QUANTUM_RESOURCES = {
'/etc/quantum/quantum.conf': {
'services': ['quantum-server'],
'contexts': [context.AMQPContext()],
}
}
2013-08-12 14:48:24 -07:00
NEUTRON_RESOURCES = {
'/etc/neutron/neutron.conf': {
'services': ['neutron-server'],
'contexts': [context.AMQPContext()],
}
}
2013-08-12 14:48:24 -07:00
# Maps virt-type config to a compute package(s).
VIRT_TYPES = {
'kvm': ['nova-compute-kvm'],
'qemu': ['nova-compute-qemu'],
'xen': ['nova-compute-xen'],
'uml': ['nova-compute-uml'],
'lxc': ['nova-compute-lxc'],
2013-07-18 19:37:30 -07:00
}
def resource_map():
'''
Dynamically generate a map of resources that will be managed for a single
hook execution.
'''
# TODO: Cache this on first call?
resource_map = deepcopy(BASE_RESOURCE_MAP)
net_manager = network_manager()
2013-08-12 14:48:24 -07:00
if (net_manager in ['flatmanager', 'flatdhcpmanager'] and
config('multi-host').lower() == 'yes'):
resource_map['/etc/nova/nova.conf']['services'].extend(
['nova-api', 'nova-network']
)
2013-08-12 14:48:24 -07:00
if net_manager in ['neutron', 'quantum']:
if net_manager == 'quantum':
resource_map.update(QUANTUM_RESOURCES)
2013-08-12 14:48:24 -07:00
if net_manager == 'neutron':
resource_map.update(NEUTRON_RESOURCES)
2013-08-12 14:48:24 -07:00
plugin = neutron_plugin()
if plugin:
2013-08-12 14:48:24 -07:00
conf = neutron_plugin_attribute(plugin, 'config', net_manager)
svcs = neutron_plugin_attribute(plugin, 'services', net_manager)
ctxts = (neutron_plugin_attribute(plugin, 'contexts', net_manager)
or [])
resource_map[conf] = {}
resource_map[conf]['services'] = svcs
resource_map[conf]['contexts'] = ctxts
2013-07-30 20:39:44 -07:00
if relation_ids('ceph'):
resource_map.update(CEPH_RESOURCES)
return resource_map
def restart_map():
'''
Constructs a restart map based on charm config settings and relation
state.
'''
return {k: v['services'] for k, v in resource_map().iteritems()}
def register_configs():
'''
2013-07-29 18:10:45 -07:00
Returns an OSTemplateRenderer object with all required configs registered.
'''
release = os_release('nova-common')
2013-07-29 18:10:45 -07:00
configs = templating.OSConfigRenderer(templates_dir=TEMPLATES,
openstack_release=release)
for cfg, d in resource_map().iteritems():
2013-07-29 18:10:45 -07:00
configs.register(cfg, d['contexts'])
return configs
def determine_packages():
packages = [] + BASE_PACKAGES
net_manager = network_manager()
2013-08-12 14:48:24 -07:00
if (net_manager in ['flatmanager', 'flatdhcpmanager'] and
config('multi-host').lower() == 'yes'):
packages.extend(['nova-api', 'nova-network'])
2013-08-12 14:48:24 -07:00
elif net_manager == 'quantum':
plugin = neutron_plugin()
packages.extend(
neutron_plugin_attribute(plugin, 'packages', net_manager))
if relation_ids('ceph'):
packages.append('ceph-common')
virt_type = config('virt-type')
try:
packages.extend(VIRT_TYPES[virt_type])
except KeyError:
log('Unsupported virt-type configured: %s' % virt_type)
raise
return packages
2013-07-18 19:37:30 -07:00
def migration_enabled():
2013-07-30 20:39:44 -07:00
# XXX: confirm juju-core bool behavior is the same.
return config('enable-live-migration')
2013-07-18 19:37:30 -07:00
def quantum_enabled():
2013-07-30 20:39:44 -07:00
manager = config('network-manager')
if not manager:
return False
return manager.lower() == 'quantum'
2013-07-18 19:37:30 -07:00
def _network_config():
'''
Obtain all relevant network configuration settings from nova-c-c via
cloud-compute interface.
'''
2013-08-12 14:48:24 -07:00
settings = ['network_manager', 'neutron_plugin', 'quantum_plugin']
net_config = {}
for rid in relation_ids('cloud-compute'):
for unit in related_units(rid):
for setting in settings:
value = relation_get(setting, rid=rid, unit=unit)
if value:
net_config[setting] = value
return net_config
2013-08-12 14:48:24 -07:00
def neutron_plugin():
return (_network_config().get('quantum_plugin') or
_network_config().get('quantum_plugin'))
def network_manager():
2013-08-12 14:48:24 -07:00
manager = _network_config().get('network_manager')
if manager:
manager = manager.lower()
return manager
2013-07-18 19:37:30 -07:00
2013-07-18 19:37:30 -07:00
def public_ssh_key(user='root'):
home = pwd.getpwnam(user).pw_dir
try:
with open(os.path.join(home, '.ssh', 'id_rsa.pub')) as key:
return key.read().strip()
except:
return None
2013-07-18 19:37:30 -07:00
def initialize_ssh_keys(user='root'):
home_dir = pwd.getpwnam(user).pw_dir
ssh_dir = os.path.join(home_dir, '.ssh')
if not os.path.isdir(ssh_dir):
os.mkdir(ssh_dir)
priv_key = os.path.join(ssh_dir, 'id_rsa')
if not os.path.isfile(priv_key):
log('Generating new ssh key for user %s.' % user)
cmd = ['ssh-keygen', '-q', '-N', '', '-t', 'rsa', '-b', '2048',
'-f', priv_key]
check_output(cmd)
pub_key = '%s.pub' % priv_key
if not os.path.isfile(pub_key):
log('Generating missing ssh public key @ %s.' % pub_key)
cmd = ['ssh-keygen', '-y', '-f', priv_key]
p = check_output(cmd).strip()
with open(pub_key, 'wb') as out:
out.write(p)
check_output(['chown', '-R', user, ssh_dir])
2013-07-18 19:37:30 -07:00
2013-07-29 12:01:44 -07:00
def import_authorized_keys(user='root'):
"""Import SSH authorized_keys + known_hosts from a cloud-compute relation
and store in user's $HOME/.ssh.
"""
# XXX: Should this be managed via templates + contexts?
hosts = relation_get('known_hosts')
auth_keys = relation_get('authorized_keys')
# XXX: Need to fix charm-helpers to return None for empty settings,
# in all cases.
if not hosts or not auth_keys:
2013-07-29 12:01:44 -07:00
return
dest = os.path.join(pwd.getpwnam(user).pw_dir, '.ssh')
log('Saving new known_hosts and authorized_keys file to: %s.' % dest)
2013-07-18 19:37:30 -07:00
with open(os.path.join(dest, 'authorized_keys'), 'wb') as _keys:
2013-07-29 12:01:44 -07:00
_keys.write(b64decode(auth_keys))
with open(os.path.join(dest, 'known_hosts'), 'wb') as _hosts:
2013-07-29 12:01:44 -07:00
_hosts.write(b64decode(hosts))
2013-07-18 19:37:30 -07:00
2013-07-18 19:37:30 -07:00
def configure_live_migration(configs=None):
"""
Ensure libvirt live migration is properly configured or disabled,
depending on current config setting.
"""
2013-07-30 20:39:44 -07:00
# dont think we need this
return
2013-07-18 19:37:30 -07:00
configs = configs or register_configs()
configs.write('/etc/libvirt/libvirtd.conf')
configs.write('/etc/default/libvirt-bin')
configs.write('/etc/nova/nova.conf')
if not migration_enabled():
return
if config('migration-auth-type') == 'ssh':
initialize_ssh_keys()
def do_openstack_upgrade():
pass
def import_keystone_ca_cert():
2013-07-29 12:01:44 -07:00
"""If provided, improt the Keystone CA cert that gets forwarded
to compute nodes via the cloud-compute interface
"""
ca_cert = relation_get('ca_cert')
if not ca_cert:
return
log('Writing Keystone CA certificate to %s' % CA_CERT_PATH)
with open(CA_CERT_PATH) as out:
out.write(b64decode(ca_cert))
check_call(['update-ca-certificates'])