348 lines
14 KiB
Python
348 lines
14 KiB
Python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
|
|
# Copyright (C) 2012 Yahoo! Inc. All Rights Reserved.
|
|
#
|
|
# 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.
|
|
|
|
import io
|
|
|
|
try:
|
|
from collections import OrderedDict
|
|
except ImportError:
|
|
from ordereddict import OrderedDict
|
|
|
|
from anvil import cfg
|
|
from anvil import colorizer
|
|
from anvil import components as comp
|
|
from anvil import exceptions
|
|
from anvil import log as logging
|
|
from anvil import shell as sh
|
|
from anvil import utils
|
|
|
|
from anvil.components.helpers import db as dbhelper
|
|
from anvil.components.helpers import keystone as khelper
|
|
from anvil.components.helpers import nova as nhelper
|
|
from anvil.components.helpers import rabbit as rhelper
|
|
from anvil.components.helpers import virt as lv
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
# Copies from helpers
|
|
API_CONF = nhelper.API_CONF
|
|
DB_NAME = nhelper.DB_NAME
|
|
PASTE_CONF = nhelper.PASTE_CONF
|
|
|
|
# Normal conf
|
|
POLICY_CONF = 'policy.json'
|
|
LOGGING_CONF = "logging.conf"
|
|
CONFIGS = [PASTE_CONF, POLICY_CONF, LOGGING_CONF, API_CONF]
|
|
ADJUST_CONFIGS = [PASTE_CONF]
|
|
|
|
# This is a special marker file that when it exists, signifies that nova net was inited
|
|
NET_INITED_FN = 'nova.network.inited.yaml'
|
|
|
|
# This makes the database be in sync with nova
|
|
DB_SYNC_CMD = [
|
|
{'cmd': ['$BIN_DIR/nova-manage', '--config-file', '$CFG_FILE', 'db', 'sync'], 'run_as_root': True},
|
|
]
|
|
|
|
# Used to create a fixed network when initializating nova
|
|
FIXED_NET_CMDS = [
|
|
{
|
|
'cmd': ['$BIN_DIR/nova-manage', '--config-file', '$CFG_FILE',
|
|
'network', 'create', 'private', '$FIXED_RANGE', '1', '$FIXED_NETWORK_SIZE'],
|
|
'run_as_root': True,
|
|
},
|
|
]
|
|
|
|
# Used to create a floating network + test floating pool
|
|
FLOATING_NET_CMDS = [
|
|
{
|
|
'cmd': ['$BIN_DIR/nova-manage', '--config-file', '$CFG_FILE', 'floating', 'create', '$FLOATING_RANGE'],
|
|
'run_as_root': True,
|
|
},
|
|
{
|
|
'cmd': ['$BIN_DIR/nova-manage', '--config-file', '$CFG_FILE',
|
|
'floating', 'create', '--ip_range=$TEST_FLOATING_RANGE', '--pool=$TEST_FLOATING_POOL'],
|
|
'run_as_root': True,
|
|
},
|
|
]
|
|
|
|
# Subdirs of the checkout/download
|
|
BIN_DIR = 'bin'
|
|
|
|
# This is a special conf
|
|
CLEANER_DATA_CONF = 'nova-clean.sh'
|
|
|
|
|
|
class NovaUninstaller(comp.PythonUninstallComponent):
|
|
def __init__(self, *args, **kargs):
|
|
comp.PythonUninstallComponent.__init__(self, *args, **kargs)
|
|
self.virsh = lv.Virsh(self.get_int_option('service_wait_seconds'), self.distro)
|
|
|
|
def pre_uninstall(self):
|
|
self._clear_libvirt_domains()
|
|
self._clean_it()
|
|
|
|
def _clean_it(self):
|
|
cleaner_fn = sh.joinpths(self.get_option('app_dir'), BIN_DIR, CLEANER_DATA_CONF)
|
|
if sh.isfile(cleaner_fn):
|
|
LOG.info("Cleaning up your system by running nova cleaner script: %s", colorizer.quote(cleaner_fn))
|
|
# These environment additions are important
|
|
# in that they eventually affect how this script runs
|
|
env = {
|
|
'ENABLED_SERVICES': ",".join(self.subsystems.keys()),
|
|
}
|
|
sh.execute(cleaner_fn, run_as_root=True, env_overrides=env)
|
|
|
|
def _clear_libvirt_domains(self):
|
|
virt_driver = nhelper.canon_virt_driver(self.get_option('virt_driver'))
|
|
if virt_driver == 'libvirt':
|
|
inst_prefix = self.get_option('instance_name_prefix', default_value='instance-')
|
|
libvirt_type = lv.canon_libvirt_type(self.get_option('libvirt_type'))
|
|
self.virsh.clear_domains(libvirt_type, inst_prefix)
|
|
|
|
|
|
class NovaInstaller(comp.PythonInstallComponent):
|
|
def __init__(self, *args, **kargs):
|
|
comp.PythonInstallComponent.__init__(self, *args, **kargs)
|
|
self.conf_maker = nhelper.ConfConfigurator(self)
|
|
|
|
@property
|
|
def config_files(self):
|
|
return list(CONFIGS)
|
|
|
|
def _filter_pip_requires_line(self, fn, line):
|
|
# We handle these ourselves in anvil
|
|
if utils.has_any(line.lower(), 'quantumclient', 'cinder', 'glance', 'python-novaclient'):
|
|
return None
|
|
return line
|
|
|
|
@property
|
|
def env_exports(self):
|
|
to_set = OrderedDict()
|
|
to_set['OS_COMPUTE_API_VERSION'] = self.get_option('nova_version')
|
|
n_params = nhelper.get_shared_params(**self.options)
|
|
for (endpoint, details) in n_params['endpoints'].items():
|
|
to_set[("NOVA_%s_URI" % (endpoint.upper()))] = details['uri']
|
|
return to_set
|
|
|
|
def verify(self):
|
|
comp.PythonInstallComponent.verify(self)
|
|
self.conf_maker.verify()
|
|
|
|
def warm_configs(self):
|
|
mq_type = nhelper.canon_mq_type(self.get_option('mq-type'))
|
|
if mq_type == 'rabbit':
|
|
rhelper.get_shared_passwords(self)
|
|
|
|
def _sync_db(self):
|
|
LOG.info("Syncing nova to database named: %s", colorizer.quote(DB_NAME))
|
|
utils.execute_template(*DB_SYNC_CMD, params=self.config_params(None))
|
|
|
|
def post_install(self):
|
|
comp.PythonInstallComponent.post_install(self)
|
|
# Extra actions to do nova setup
|
|
if self.get_bool_option('db-sync'):
|
|
self._setup_db()
|
|
self._sync_db()
|
|
self._setup_cleaner()
|
|
|
|
def _setup_cleaner(self):
|
|
LOG.info("Configuring cleaner template: %s", colorizer.quote(CLEANER_DATA_CONF))
|
|
(_fn, contents) = utils.load_template(self.name, CLEANER_DATA_CONF)
|
|
# FIXME(harlowja), stop placing in checkout dir...
|
|
cleaner_fn = sh.joinpths(sh.joinpths(self.get_option('app_dir'), BIN_DIR), CLEANER_DATA_CONF)
|
|
sh.write_file(cleaner_fn, contents)
|
|
sh.chmod(cleaner_fn, 0755)
|
|
self.tracewriter.file_touched(cleaner_fn)
|
|
|
|
def _setup_db(self):
|
|
dbhelper.drop_db(distro=self.distro,
|
|
dbtype=self.get_option('db', 'type'),
|
|
dbname=DB_NAME,
|
|
**utils.merge_dicts(self.get_option('db'),
|
|
dbhelper.get_shared_passwords(self)))
|
|
# Explicitly use latin1: to avoid lp#829209, nova expects the database to
|
|
# use latin1 by default, and then upgrades the database to utf8 (see the
|
|
# 082_essex.py in nova)
|
|
dbhelper.create_db(distro=self.distro,
|
|
dbtype=self.get_option('db', 'type'),
|
|
dbname=DB_NAME,
|
|
charset='latin1',
|
|
**utils.merge_dicts(self.get_option('db'),
|
|
dbhelper.get_shared_passwords(self)))
|
|
|
|
def _generate_nova_conf(self, fn):
|
|
LOG.debug("Generating dynamic content for nova: %s.", (fn))
|
|
return self.conf_maker.generate(fn)
|
|
|
|
def source_config(self, config_fn):
|
|
if config_fn == PASTE_CONF:
|
|
config_fn = 'api-paste.ini'
|
|
elif config_fn == LOGGING_CONF:
|
|
config_fn = 'logging_sample.conf'
|
|
elif config_fn == API_CONF:
|
|
config_fn = 'nova.conf.sample'
|
|
fn = sh.joinpths(self.get_option('app_dir'), 'etc', "nova", config_fn)
|
|
return (fn, sh.load_file(fn))
|
|
|
|
def _config_adjust_paste(self, contents, fn):
|
|
params = khelper.get_shared_params(ip=self.get_option('ip'),
|
|
service_user='nova',
|
|
**utils.merge_dicts(self.get_option('keystone'),
|
|
khelper.get_shared_passwords(self)))
|
|
|
|
with io.BytesIO(contents) as stream:
|
|
config = cfg.RewritableConfigParser()
|
|
config.readfp(stream)
|
|
|
|
config.set('filter:authtoken', 'auth_host', params['endpoints']['admin']['host'])
|
|
config.set('filter:authtoken', 'auth_port', params['endpoints']['admin']['port'])
|
|
config.set('filter:authtoken', 'auth_protocol', params['endpoints']['admin']['protocol'])
|
|
|
|
config.set('filter:authtoken', 'service_host', params['endpoints']['internal']['host'])
|
|
config.set('filter:authtoken', 'service_port', params['endpoints']['internal']['port'])
|
|
config.set('filter:authtoken', 'service_protocol', params['endpoints']['internal']['protocol'])
|
|
|
|
config.set('filter:authtoken', 'admin_tenant_name', params['service_tenant'])
|
|
config.set('filter:authtoken', 'admin_user', params['service_user'])
|
|
config.set('filter:authtoken', 'admin_password', params['service_password'])
|
|
|
|
contents = config.stringify(fn)
|
|
return contents
|
|
|
|
def _config_adjust_logging(self, contents, fn):
|
|
with io.BytesIO(contents) as stream:
|
|
config = cfg.RewritableConfigParser()
|
|
config.readfp(stream)
|
|
config.set('logger_root', 'level', 'DEBUG')
|
|
config.set('logger_root', 'handlers', "stdout")
|
|
contents = config.stringify(fn)
|
|
return contents
|
|
|
|
def _config_adjust(self, contents, name):
|
|
if name == PASTE_CONF:
|
|
return self._config_adjust_paste(contents, name)
|
|
elif name == LOGGING_CONF:
|
|
return self._config_adjust_logging(contents, name)
|
|
elif name == API_CONF:
|
|
return self._generate_nova_conf(name)
|
|
else:
|
|
return contents
|
|
|
|
def _config_param_replace(self, config_fn, contents, parameters):
|
|
if config_fn in [PASTE_CONF, LOGGING_CONF, API_CONF]:
|
|
# We handle these ourselves
|
|
return contents
|
|
else:
|
|
return comp.PythonInstallComponent._config_param_replace(self, config_fn, contents, parameters)
|
|
|
|
def config_params(self, config_fn):
|
|
mp = comp.PythonInstallComponent.config_params(self, config_fn)
|
|
mp['CFG_FILE'] = sh.joinpths(self.get_option('cfg_dir'), API_CONF)
|
|
mp['BIN_DIR'] = sh.joinpths(self.get_option('app_dir'), BIN_DIR)
|
|
return mp
|
|
|
|
|
|
class NovaRuntime(comp.PythonRuntime):
|
|
def __init__(self, *args, **kargs):
|
|
comp.PythonRuntime.__init__(self, *args, **kargs)
|
|
self.wait_time = self.get_int_option('service_wait_seconds')
|
|
self.virsh = lv.Virsh(self.wait_time, self.distro)
|
|
self.config_path = sh.joinpths(self.get_option('cfg_dir'), API_CONF)
|
|
self.bin_dir = sh.joinpths(self.get_option('app_dir'), BIN_DIR)
|
|
self.net_init_fn = sh.joinpths(self.get_option('trace_dir'), NET_INITED_FN)
|
|
|
|
def _do_network_init(self):
|
|
ran_fn = self.net_init_fn
|
|
if not sh.isfile(ran_fn) and self.get_bool_option('do-network-init'):
|
|
# Figure out the commands to run
|
|
cmds = []
|
|
mp = {
|
|
'CFG_FILE': self.config_path,
|
|
'BIN_DIR': self.bin_dir
|
|
}
|
|
mp['BIN_DIR'] = self.bin_dir
|
|
if self.get_bool_option('enable_fixed'):
|
|
# Create a fixed network
|
|
mp['FIXED_NETWORK_SIZE'] = self.get_option('fixed_network_size', default_value='256')
|
|
mp['FIXED_RANGE'] = self.get_option('fixed_range', default_value='10.0.0.0/24')
|
|
cmds.extend(FIXED_NET_CMDS)
|
|
if self.get_bool_option('enable_floating'):
|
|
# Create a floating network + test floating pool
|
|
cmds.extend(FLOATING_NET_CMDS)
|
|
mp['FLOATING_RANGE'] = self.get_option('floating_range', default_value='172.24.4.224/28')
|
|
mp['TEST_FLOATING_RANGE'] = self.get_option('test_floating_range', default_value='192.168.253.0/29')
|
|
mp['TEST_FLOATING_POOL'] = self.get_option('test_floating_pool', default_value='test')
|
|
# Anything to run??
|
|
if cmds:
|
|
LOG.info("Creating your nova network to be used with instances.")
|
|
utils.execute_template(*cmds, params=mp)
|
|
# Writing this makes sure that we don't init again
|
|
cmd_mp = {
|
|
'cmds': cmds,
|
|
'replacements': mp,
|
|
}
|
|
sh.write_file(ran_fn, utils.prettify_yaml(cmd_mp))
|
|
LOG.info("If you wish to re-run network initialization, delete %s", colorizer.quote(ran_fn))
|
|
|
|
def post_start(self):
|
|
self._do_network_init()
|
|
|
|
@property
|
|
def apps_to_start(self):
|
|
apps = []
|
|
for (name, _values) in self.subsystems.items():
|
|
real_name = "nova-%s" % (name)
|
|
app_pth = sh.joinpths(self.bin_dir, real_name)
|
|
if sh.is_executable(app_pth):
|
|
apps.append({
|
|
'name': real_name,
|
|
'path': app_pth,
|
|
})
|
|
return apps
|
|
|
|
def pre_start(self):
|
|
# Let the parent class do its thing
|
|
comp.PythonRuntime.pre_start(self)
|
|
virt_driver = nhelper.canon_virt_driver(self.get_option('virt_driver'))
|
|
if virt_driver == 'libvirt':
|
|
virt_type = lv.canon_libvirt_type(self.get_option('libvirt_type'))
|
|
LOG.info("Checking that your selected libvirt virtualization type %s is working and running.", colorizer.quote(virt_type))
|
|
try:
|
|
self.virsh.check_virt(virt_type)
|
|
self.virsh.restart_service()
|
|
LOG.info("Libvirt virtualization type %s seems to be working and running.", colorizer.quote(virt_type))
|
|
except exceptions.ProcessExecutionError as e:
|
|
msg = ("Libvirt type %r does not seem to be active or configured correctly, "
|
|
"perhaps you should be using %r instead: %s" %
|
|
(virt_type, lv.DEF_VIRT_TYPE, e))
|
|
raise exceptions.StartException(msg)
|
|
|
|
def app_params(self, app_name):
|
|
params = comp.PythonRuntime.app_params(self, app_name)
|
|
params['CFG_FILE'] = self.config_path
|
|
return params
|
|
|
|
def app_options(self, app):
|
|
return ['--config-file', '$CFG_FILE']
|
|
|
|
|
|
class NovaTester(comp.PythonTestingComponent):
|
|
def _get_test_exclusions(self):
|
|
return [
|
|
# Disable since quantumclient is not always installed.
|
|
'test_quantumv2',
|
|
]
|