@ -30,7 +30,8 @@ from os import path
from init . shell import ( check , call , check_output , shell , sql , nc_wait ,
log_wait , restart , download )
from init . config import Env , log
from init . question import Question
from init . questions . question import Question
from init . questions import clustering
_env = Env ( ) . get_env ( )
@ -43,6 +44,69 @@ class ConfigError(Exception):
"""
class Clustering ( Question ) :
""" Possibly setup clustering. """
_type = ' boolean '
_question = ' Do you want to setup clustering? '
config_key = ' config.clustered '
interactive = True
def yes ( self , answer : bool ) :
log . info ( ' Configuring clustering ... ' )
questions = [
clustering . Role ( ) ,
clustering . Password ( ) ,
clustering . ControlIp ( ) ,
clustering . ComputeIp ( ) , # Automagically skipped role='control'
]
for question in questions :
if not self . interactive :
question . interactive = False
question . ask ( )
role = check_output ( ' snapctl ' , ' get ' , ' config.cluster.role ' )
control_ip = check_output ( ' snapctl ' , ' get ' ,
' config.network.control-ip ' )
password = check_output ( ' snapctl ' , ' get ' , ' config.cluster.password ' )
log . debug ( ' Role: {} , IP: {} , Password: {} ' . format (
role , control_ip , password ) )
# TODO: raise an exception if any of the above are None (can
# happen if we're automatig and mess up our params.)
if role == ' compute ' :
log . info ( ' I am a compute node. ' )
# Gets config info and sets local env vals.
check_output ( ' microstack_join ' )
# Set default question answers.
check ( ' snapctl ' , ' set ' , ' config.services.control-plane=false ' )
check ( ' snapctl ' , ' set ' , ' config.services.hypervisor=true ' )
if role == ' control ' :
log . info ( ' I am a control node. ' )
check ( ' snapctl ' , ' set ' , ' config.services.control-plane=true ' )
# We want to run a hypervisor on our control plane nodes
# -- this is essentially a hyper converged cloud.
check ( ' snapctl ' , ' set ' , ' config.services.hypervisor=true ' )
# TODO: if this is run after init has already been called,
# need to restart services.
# Write templates
check ( ' snap-openstack ' , ' setup ' )
def no ( self , answer : bool ) :
# Turn off cluster server
# TODO: it would be more secure to reverse this -- only enable
# to service if we are doing clustering.
check ( ' systemctl ' , ' disable ' , ' snap.microstack.cluster-server ' )
class ConfigQuestion ( Question ) :
""" Question class that simply asks for and sets a config value.
@ -78,6 +142,7 @@ class Dns(Question):
_type = ' string '
_question = ' DNS to use '
config_key = ' config.network.dns '
def yes ( self , answer : str ) :
""" Override the default dhcp_agent.ini file. """
@ -105,11 +170,21 @@ class ExtGateway(ConfigQuestion):
_type = ' string '
_question = ' External Gateway '
config_key = ' config.network.ext-gateway '
def yes ( self , answer ) :
# Preserve old behavior.
# TODO: update this
_env [ ' extgateway ' ] = answer
clustered = check_output ( ' snapctl ' , ' get ' , ' config.clustered ' )
if clustered . lower ( ) != ' true ' :
check ( ' snapctl ' , ' set ' , ' config.network.control-ip= {} ' . format (
answer ) )
check ( ' snapctl ' , ' set ' , ' config.network.compute-ip= {} ' . format (
answer ) )
_env [ ' control_ip ' ] = _env [ ' compute_ip ' ] = answer
else :
_env [ ' control_ip ' ] = check_output ( ' snapctl ' , ' get ' ,
' config.network.control-ip ' )
_env [ ' compute_ip ' ] = check_output ( ' snapctl ' , ' get ' ,
' config.network.compute-ip ' )
class ExtCidr ( ConfigQuestion ) :
@ -117,20 +192,18 @@ class ExtCidr(ConfigQuestion):
_type = ' string '
_question = ' External Ip Range '
config_key = ' config.network.ext-cidr '
def yes ( self , answer ) :
# Preserve old behavior.
# TODO: update this
_env [ ' extcidr ' ] = answer
class OsPassword ( ConfigQuestion ) :
_type = ' string '
_question = ' Openstack Admin Password '
config_key = ' config.credentials.os-password '
def yes ( self , answer ) :
# Preserve old behavior.
# TODO: update this
_env [ ' ospassword ' ] = answer
# TODO obfuscate the password!
@ -141,6 +214,7 @@ class IpForwarding(Question):
_type = ' boolean ' # Auto for now, to maintain old behavior.
_question = ' Do you wish to setup ip forwarding? (recommended) '
config_key = ' config.host.ip-forwarding '
def yes ( self , answer : str ) - > None :
""" Use sysctl to setup ip forwarding. """
@ -150,7 +224,8 @@ class IpForwarding(Question):
class ForceQemu ( Question ) :
_type = ' auto '
_type = ' boolean '
config_key = ' config.host.check-qemu '
def yes ( self , answer : str ) - > None :
""" Possibly force us to use qemu emulation rather than kvm. """
@ -204,9 +279,10 @@ class RabbitMq(Question):
""" Wait for Rabbit to start, then setup permissions. """
_type = ' boolean '
config_key = ' config.services.control-plane '
def _wait ( self ) - > None :
nc_wait ( _env [ ' extgateway ' ] , ' 5672 ' )
nc_wait ( _env [ ' control_ip ' ] , ' 5672 ' )
log_file = ' {SNAP_COMMON} /log/rabbitmq/startup_log ' . format ( * * _env )
log_wait ( log_file , ' completed ' )
@ -230,31 +306,42 @@ class RabbitMq(Question):
self . _configure ( )
log . info ( ' RabbitMQ Configured! ' )
def no ( self , answer : str ) :
log . info ( ' Disabling local rabbit ... ' )
check ( ' systemctl ' , ' disable ' , ' snap.microstack.rabbitmq-server ' )
class DatabaseSetup ( Question ) :
""" Setup keystone permissions, then setup all databases. """
_type = ' boolean '
config_key = ' config.services.control-plane '
def _wait ( self ) - > None :
nc_wait ( _env [ ' extgateway ' ] , ' 3306 ' )
nc_wait ( _env [ ' control_ip ' ] , ' 3306 ' )
log_wait ( ' {SNAP_COMMON} /log/mysql/error.log ' . format ( * * _env ) ,
' mysqld: ready for connections. ' )
def _create_dbs ( self ) - > None :
# TODO: actually use passwords here.
for db in ( ' neutron ' , ' nova ' , ' nova_api ' , ' nova_cell0 ' , ' cinder ' ,
' glance ' , ' keystone ' ) :
sql ( " CREATE DATABASE IF NOT EXISTS {db} ; " . format ( db = db ) )
sql (
" GRANT ALL PRIVILEGES ON {db} .* TO {db} @ {extgateway } \
" GRANT ALL PRIVILEGES ON {db} .* TO {db} @ {control_ip } \
IDENTIFIED BY ' {db} ' ; " .format(db=db, **_env))
# Grant nova user access to cell0
sql (
" GRANT ALL PRIVILEGES ON nova_cell0.* TO ' nova ' @ ' {control_ip} ' \
IDENTIFIED BY \' nova ' ; " .format(**_env))
def _bootstrap ( self ) - > None :
if call ( ' openstack ' , ' user ' , ' show ' , ' admin ' ) :
return
bootstrap_url = ' http:// {extgateway } :5000/v3/ ' . format ( * * _env )
bootstrap_url = ' http:// {control_ip } :5000/v3/ ' . format ( * * _env )
check ( ' snap-openstack ' , ' launch ' , ' keystone-manage ' , ' bootstrap ' ,
' --bootstrap-password ' , _env [ ' ospassword ' ] ,
@ -300,11 +387,45 @@ class DatabaseSetup(Question):
log . info ( ' Keystone configured! ' )
def no ( self , answer : str ) :
# We assume that the control node has a connection setup for us.
check ( ' snapctl ' , ' set ' , ' database.ready=true ' )
log . info ( ' Disabling local MySQL ... ' )
check ( ' systemctl ' , ' disable ' , ' snap.microstack.mysqld ' )
class NovaHypervisor ( Question ) :
""" Run the nova compute hypervisor. """
_type = ' boolean '
config_key = ' config.services.hypervisor '
def yes ( self , answer ) :
log . info ( ' Configuring nova compute hypervisor ... ' )
if not call ( ' openstack ' , ' service ' , ' show ' , ' compute ' ) :
check ( ' openstack ' , ' service ' , ' create ' , ' --name ' , ' nova ' ,
' --description ' , ' " Openstack Compute " ' , ' compute ' )
# TODO make sure that we are the control plane before executing
# TODO if control plane is not hypervisor, still create this
for endpoint in [ ' public ' , ' internal ' , ' admin ' ] :
call ( ' openstack ' , ' endpoint ' , ' create ' , ' --region ' ,
' microstack ' , ' compute ' , endpoint ,
' http:// {compute_ip} :8774/v2.1 ' . format ( * * _env ) )
check ( ' snapctl ' , ' start ' , ' microstack.nova-compute ' )
def no ( self , answer ) :
log . info ( ' Disabling nova compute service ... ' )
check ( ' systemctl ' , ' disable ' , ' snap.microstack.nova-compute ' )
class NovaSetup ( Question ) :
""" Create all relevant nova users and services. """
class NovaControlPlane ( Question ) :
""" Create all control plane nova users and services. """
_type = ' boolean '
config_key = ' config.services.control-plane '
def _flavors ( self ) - > None :
""" Create default flavors. """
@ -327,7 +448,7 @@ class NovaSetup(Question):
' m1.xlarge ' )
def yes ( self , answer : str ) - > None :
log . info ( ' Configuring nova ... ' )
log . info ( ' Configuring nova control plane services ... ' )
if not call ( ' openstack ' , ' user ' , ' show ' , ' nova ' ) :
check ( ' openstack ' , ' user ' , ' create ' , ' --domain ' ,
@ -341,14 +462,6 @@ class NovaSetup(Question):
check ( ' openstack ' , ' role ' , ' add ' , ' --project ' , ' service ' ,
' --user ' , ' placement ' , ' admin ' )
if not call ( ' openstack ' , ' service ' , ' show ' , ' compute ' ) :
check ( ' openstack ' , ' service ' , ' create ' , ' --name ' , ' nova ' ,
' --description ' , ' " Openstack Compute " ' , ' compute ' )
for endpoint in [ ' public ' , ' internal ' , ' admin ' ] :
call ( ' openstack ' , ' endpoint ' , ' create ' , ' --region ' ,
' microstack ' , ' compute ' , endpoint ,
' http:// {extgateway} :8774/v2.1 ' . format ( * * _env ) )
if not call ( ' openstack ' , ' service ' , ' show ' , ' placement ' ) :
check ( ' openstack ' , ' service ' , ' create ' , ' --name ' ,
' placement ' , ' --description ' , ' " Placement API " ' ,
@ -357,12 +470,7 @@ class NovaSetup(Question):
for endpoint in [ ' public ' , ' internal ' , ' admin ' ] :
call ( ' openstack ' , ' endpoint ' , ' create ' , ' --region ' ,
' microstack ' , ' placement ' , endpoint ,
' http:// {extgateway} :8778 ' . format ( * * _env ) )
# Grant nova user access to cell0
sql (
" GRANT ALL PRIVILEGES ON nova_cell0.* TO ' nova ' @ ' {extgateway} ' \
IDENTIFIED BY \' nova ' ; " .format(**_env))
' http:// {control_ip} :8778 ' . format ( * * _env ) )
# Use snapctl to start nova services. We need to call them
# out manually, because systemd doesn't know about them yet.
@ -371,7 +479,6 @@ class NovaSetup(Question):
for service in [
' microstack.nova-api ' ,
' microstack.nova-api-metadata ' ,
' microstack.nova-compute ' ,
' microstack.nova-conductor ' ,
' microstack.nova-scheduler ' ,
' microstack.nova-uwsgi ' ,
@ -396,18 +503,31 @@ class NovaSetup(Question):
restart ( ' nova-* ' )
nc_wait ( _env [ ' extgateway ' ] , ' 8774 ' )
nc_wait ( _env [ ' compute_ip ' ] , ' 8774 ' )
sleep ( 5 ) # TODO: log_wait
log . info ( ' Creating default flavors... ' )
self . _flavors ( )
def no ( self , answer ) :
log . info ( ' Disabling nova control plane services ... ' )
for service in [
' snap.microstack.nova-uwsgi ' ,
' snap.microstack.nova-api ' ,
' snap.microstack.nova-conductor ' ,
' snap.microstack.nova-scheduler ' ,
' snap.microstack.nova-api-metadata ' ] :
check ( ' systemctl ' , ' disable ' , service )
class NeutronSetup ( Question ) :
class NeutronControlPlane ( Question ) :
""" Create all relevant neutron services and users. """
_type = ' boolean '
config_key = ' config.services.control-plane '
def yes ( self , answer : str ) - > None :
log . info ( ' Configuring Neutron ' )
@ -424,7 +544,7 @@ class NeutronSetup(Question):
for endpoint in [ ' public ' , ' internal ' , ' admin ' ] :
call ( ' openstack ' , ' endpoint ' , ' create ' , ' --region ' ,
' microstack ' , ' network ' , endpoint ,
' http:// {extgateway } :9696 ' . format ( * * _env ) )
' http:// {control_ip } :9696 ' . format ( * * _env ) )
for service in [
' microstack.neutron-api ' ,
@ -440,7 +560,7 @@ class NeutronSetup(Question):
restart ( ' neutron-* ' )
nc_wait ( _env [ ' extgateway ' ] , ' 9696 ' )
nc_wait ( _env [ ' control_ip ' ] , ' 9696 ' )
sleep ( 5 ) # TODO: log_wait
@ -467,27 +587,49 @@ class NeutronSetup(Question):
check ( ' openstack ' , ' router ' , ' set ' , ' --external-gateway ' ,
' external ' , ' test-router ' )
def no ( self , answer ) :
""" Create endpoints pointed at control node if we ' re not setting up
neutron on this machine .
"""
# Make sure that the agent is running.
for service in [
' microstack.neutron-openvswitch-agent ' ,
] :
check ( ' snapctl ' , ' start ' , service )
# Disable the other services.
for service in [
' snap.microstack.neutron-api ' ,
' snap.microstack.neutron-dhcp-agent ' ,
' snap.microstack.neutron-metadata-agent ' ,
' snap.microstack.neutron-l3-agent ' ,
] :
check ( ' systemctl ' , ' disable ' , service )
class GlanceSetup ( Question ) :
""" Setup glance, and download an initial Cirros image. """
_type = ' boolean '
config_key = ' config.services.control-plane '
def _fetch_cirros ( self ) - > None :
if call ( ' openstack ' , ' image ' , ' show ' , ' cirros ' ) :
return
log . info ( ' Adding cirros image ... ' )
env = dict ( * * _env )
env [ ' VER ' ] = ' 0.4.0 '
env [ ' IMG ' ] = ' cirros- {VER} -x86_64-disk.img ' . format ( * * env )
log . info ( ' Fetching cirros image ... ' )
cirros_path = ' {SNAP_COMMON} /images/ {IMG} ' . format ( * * env )
if not path . exists ( cirros_path ) :
check ( ' mkdir ' , ' -p ' , ' {SNAP_COMMON} /images ' . format ( * * env ) )
log . info ( ' Downloading cirros image ... ' )
download (
' http://download.cirros-cloud.net/ {VER} / {IMG} ' . format ( * * env ) ,
' {SNAP_COMMON} /images/ {IMG} ' . format ( * * env ) )
@ -513,11 +655,11 @@ class GlanceSetup(Question):
for endpoint in [ ' internal ' , ' admin ' , ' public ' ] :
check ( ' openstack ' , ' endpoint ' , ' create ' , ' --region ' ,
' microstack ' , ' image ' , endpoint ,
' http:// {extgateway } :9292 ' . format ( * * _env ) )
' http:// {compute_ip } :9292 ' . format ( * * _env ) )
for service in [
' microstack.glance-api ' ,
' microstack.registry ' , # TODO rename this t o glance-registery
' microstack.registry ' , # TODO rename to glance-registery
] :
check ( ' snapctl ' , ' start ' , service )
@ -525,12 +667,16 @@ class GlanceSetup(Question):
restart ( ' glance* ' )