Merge pull request #99 from dreamhost/clean-up-password-prompts

Move the password prompts back out of the central location
This commit is contained in:
Joshua Harlow 2012-03-15 12:56:31 -07:00
commit 51254e64d1
9 changed files with 116 additions and 75 deletions

View File

@ -14,7 +14,6 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from devstack import exceptions as excp
from devstack import log as logging from devstack import log as logging
from devstack import settings from devstack import settings
@ -31,39 +30,7 @@ def make_id(section, option):
def fetch_run_type(config): def fetch_run_type(config):
run_type = config.getdefaulted("default", "run_type", settings.RUN_TYPE_DEF) run_type = config.getdefaulted("default", "run_type",
settings.RUN_TYPE_DEF)
run_type = run_type.upper() run_type = run_type.upper()
return run_type return run_type
def fetch_dbdsn(config, pw_gen, dbname=''):
#check the dsn cache
user = config.get("db", "sql_user")
host = config.get("db", "sql_host")
port = config.get("db", "port")
pw = pw_gen.get_password("sql")
#form the dsn (from components we have...)
#dsn = "<driver>://<username>:<password>@<host>:<port>/<database>"
if not host:
msg = "Unable to fetch a database dsn - no sql host found"
raise excp.BadParamException(msg)
driver = config.get("db", "type")
if not driver:
msg = "Unable to fetch a database dsn - no db driver type found"
raise excp.BadParamException(msg)
dsn = driver + "://"
if user:
dsn += user
if pw:
dsn += ":" + pw
if user or pw:
dsn += "@"
dsn += host
if port:
dsn += ":" + port
if dbname:
dsn += "/" + dbname
else:
dsn += "/"
LOG.debug("For database [%s] fetched dsn [%s]" % (dbname, dsn))
return dsn

View File

@ -22,6 +22,7 @@ from devstack import shell as sh
from devstack import utils from devstack import utils
#id #id
# FIXME: This should probably come from the persona
TYPE = settings.DB TYPE = settings.DB
LOG = logging.getLogger("devstack.components.db") LOG = logging.getLogger("devstack.components.db")
@ -43,7 +44,8 @@ SQL_RESET_PW_LINKS = [
BASE_ERROR = 'Currently we do not know how to [%s] for database type [%s]' BASE_ERROR = 'Currently we do not know how to [%s] for database type [%s]'
#config keys we warm up so u won't be prompted later #config keys we warm up so u won't be prompted later
WARMUP_PWS = ['sql'] PASSWORD_PROMPT = 'the database user'
WARMUP_PWS = [('sql', PASSWORD_PROMPT)]
class DBUninstaller(comp.PkgUninstallComponent): class DBUninstaller(comp.PkgUninstallComponent):
@ -52,8 +54,8 @@ class DBUninstaller(comp.PkgUninstallComponent):
self.runtime = DBRuntime(*args, **kargs) self.runtime = DBRuntime(*args, **kargs)
def warm_configs(self): def warm_configs(self):
for pw_key in WARMUP_PWS: for key, prompt in WARMUP_PWS:
self.pw_gen.get_password(pw_key) self.pw_gen.get_password(key, prompt)
def pre_uninstall(self): def pre_uninstall(self):
dbtype = self.cfg.get("db", "type") dbtype = self.cfg.get("db", "type")
@ -67,7 +69,7 @@ class DBUninstaller(comp.PkgUninstallComponent):
LOG.info("Ensuring your database is started before we operate on it.") LOG.info("Ensuring your database is started before we operate on it.")
self.runtime.restart() self.runtime.restart()
params = { params = {
'OLD_PASSWORD': self.pw_gen.get_password('sql'), 'OLD_PASSWORD': self.pw_gen.get_password('sql', PASSWORD_PROMPT),
'NEW_PASSWORD': RESET_BASE_PW, 'NEW_PASSWORD': RESET_BASE_PW,
'USER': self.cfg.getdefaulted("db", "sql_user", 'root'), 'USER': self.cfg.getdefaulted("db", "sql_user", 'root'),
} }
@ -89,7 +91,7 @@ class DBInstaller(comp.PkgInstallComponent):
#in pre-install and post-install sections #in pre-install and post-install sections
host_ip = self.cfg.get('host', 'ip') host_ip = self.cfg.get('host', 'ip')
out = { out = {
'PASSWORD': self.pw_gen.get_password("sql"), 'PASSWORD': self.pw_gen.get_password("sql", PASSWORD_PROMPT),
'BOOT_START': ("%s" % (True)).lower(), 'BOOT_START': ("%s" % (True)).lower(),
'USER': self.cfg.getdefaulted("db", "sql_user", 'root'), 'USER': self.cfg.getdefaulted("db", "sql_user", 'root'),
'SERVICE_HOST': host_ip, 'SERVICE_HOST': host_ip,
@ -98,8 +100,8 @@ class DBInstaller(comp.PkgInstallComponent):
return out return out
def warm_configs(self): def warm_configs(self):
for pw_key in WARMUP_PWS: for key, prompt in WARMUP_PWS:
self.pw_gen.get_password(pw_key) self.pw_gen.get_password(key, prompt)
def _configure_db_confs(self): def _configure_db_confs(self):
dbtype = self.cfg.get("db", "type") dbtype = self.cfg.get("db", "type")
@ -143,7 +145,7 @@ class DBInstaller(comp.PkgInstallComponent):
LOG.info("Ensuring your database is started before we operate on it.") LOG.info("Ensuring your database is started before we operate on it.")
self.runtime.restart() self.runtime.restart()
params = { params = {
'NEW_PASSWORD': self.pw_gen.get_password("sql"), 'NEW_PASSWORD': self.pw_gen.get_password("sql", PASSWORD_PROMPT),
'USER': self.cfg.getdefaulted("db", "sql_user", 'root'), 'USER': self.cfg.getdefaulted("db", "sql_user", 'root'),
'OLD_PASSWORD': RESET_BASE_PW, 'OLD_PASSWORD': RESET_BASE_PW,
} }
@ -162,7 +164,7 @@ class DBInstaller(comp.PkgInstallComponent):
LOG.info("Ensuring your database is started before we operate on it.") LOG.info("Ensuring your database is started before we operate on it.")
self.runtime.restart() self.runtime.restart()
params = { params = {
'PASSWORD': self.pw_gen.get_password("sql"), 'PASSWORD': self.pw_gen.get_password("sql", PASSWORD_PROMPT),
'USER': user, 'USER': user,
} }
cmds = [{'cmd': grant_cmd}] cmds = [{'cmd': grant_cmd}]
@ -243,7 +245,7 @@ def drop_db(cfg, pw_gen, distro, dbname):
if dbactions and dbactions.get('drop_db'): if dbactions and dbactions.get('drop_db'):
dropcmd = dbactions.get('drop_db') dropcmd = dbactions.get('drop_db')
params = dict() params = dict()
params['PASSWORD'] = pw_gen.get_password("sql") params['PASSWORD'] = pw_gen.get_password("sql", PASSWORD_PROMPT)
params['USER'] = cfg.getdefaulted("db", "sql_user", 'root') params['USER'] = cfg.getdefaulted("db", "sql_user", 'root')
params['DB'] = dbname params['DB'] = dbname
cmds = list() cmds = list()
@ -263,7 +265,7 @@ def create_db(cfg, pw_gen, distro, dbname):
if dbactions and dbactions.get('create_db'): if dbactions and dbactions.get('create_db'):
createcmd = dbactions.get('create_db') createcmd = dbactions.get('create_db')
params = dict() params = dict()
params['PASSWORD'] = pw_gen.get_password("sql") params['PASSWORD'] = pw_gen.get_password("sql", PASSWORD_PROMPT)
params['USER'] = cfg.getdefaulted("db", "sql_user", 'root') params['USER'] = cfg.getdefaulted("db", "sql_user", 'root')
params['DB'] = dbname params['DB'] = dbname
cmds = list() cmds = list()
@ -275,3 +277,36 @@ def create_db(cfg, pw_gen, distro, dbname):
else: else:
msg = BASE_ERROR % ('create', dbtype) msg = BASE_ERROR % ('create', dbtype)
raise NotImplementedError(msg) raise NotImplementedError(msg)
def fetch_dbdsn(config, pw_gen, dbname=''):
"""Return the database connection string, including password."""
user = config.get("db", "sql_user")
host = config.get("db", "sql_host")
port = config.get("db", "port")
pw = pw_gen.get_password("sql", PASSWORD_PROMPT)
#form the dsn (from components we have...)
#dsn = "<driver>://<username>:<password>@<host>:<port>/<database>"
if not host:
msg = "Unable to fetch a database dsn - no sql host found"
raise excp.BadParamException(msg)
driver = config.get("db", "type")
if not driver:
msg = "Unable to fetch a database dsn - no db driver type found"
raise excp.BadParamException(msg)
dsn = driver + "://"
if user:
dsn += user
if pw:
dsn += ":" + pw
if user or pw:
dsn += "@"
dsn += host
if port:
dsn += ":" + port
if dbname:
dsn += "/" + dbname
else:
dsn += "/"
LOG.debug("For database [%s] fetched dsn [%s]" % (dbname, dsn))
return dsn

View File

@ -17,7 +17,6 @@
import io import io
from devstack import cfg from devstack import cfg
from devstack import cfg_helpers
from devstack import component as comp from devstack import component as comp
from devstack import log as logging from devstack import log as logging
from devstack import settings from devstack import settings
@ -173,7 +172,7 @@ class GlanceInstaller(comp.PythonInstallComponent):
mp = dict() mp = dict()
mp['DEST'] = self.appdir mp['DEST'] = self.appdir
mp['SYSLOG'] = self.cfg.getboolean("default", "syslog") mp['SYSLOG'] = self.cfg.getboolean("default", "syslog")
mp['SQL_CONN'] = cfg_helpers.fetch_dbdsn(self.cfg, self.pw_gen, DB_NAME) mp['SQL_CONN'] = db.fetch_dbdsn(self.cfg, self.pw_gen, DB_NAME)
mp['SERVICE_HOST'] = self.cfg.get('host', 'ip') mp['SERVICE_HOST'] = self.cfg.get('host', 'ip')
mp['HOST_IP'] = self.cfg.get('host', 'ip') mp['HOST_IP'] = self.cfg.get('host', 'ip')
mp.update(keystone.get_shared_params(self.cfg, self.pw_gen, 'glance')) mp.update(keystone.get_shared_params(self.cfg, self.pw_gen, 'glance'))

View File

@ -19,7 +19,6 @@ import io
from urlparse import urlunparse from urlparse import urlunparse
from devstack import cfg from devstack import cfg
from devstack import cfg_helpers
from devstack import component as comp from devstack import component as comp
from devstack import log as logging from devstack import log as logging
from devstack import settings from devstack import settings
@ -192,7 +191,7 @@ class KeystoneInstaller(comp.PythonInstallComponent):
mp['BIN_DIR'] = self.bindir mp['BIN_DIR'] = self.bindir
mp['CONFIG_FILE'] = sh.joinpths(self.cfgdir, ROOT_CONF) mp['CONFIG_FILE'] = sh.joinpths(self.cfgdir, ROOT_CONF)
if config_fn == ROOT_CONF: if config_fn == ROOT_CONF:
mp['SQL_CONN'] = cfg_helpers.fetch_dbdsn(self.cfg, self.pw_gen, DB_NAME) mp['SQL_CONN'] = db.fetch_dbdsn(self.cfg, self.pw_gen, DB_NAME)
mp['KEYSTONE_DIR'] = self.appdir mp['KEYSTONE_DIR'] = self.appdir
mp.update(get_shared_params(self.cfg, self.pw_gen)) mp.update(get_shared_params(self.cfg, self.pw_gen))
elif config_fn == MANAGE_DATA_CONF: elif config_fn == MANAGE_DATA_CONF:
@ -250,9 +249,19 @@ def get_shared_params(config, pw_gen, service_user_name=None):
mp['DEMO_TENANT_NAME'] = mp['DEMO_USER_NAME'] mp['DEMO_TENANT_NAME'] = mp['DEMO_USER_NAME']
#tokens and passwords #tokens and passwords
mp['SERVICE_TOKEN'] = pw_gen.get_password("service_token") mp['SERVICE_TOKEN'] = pw_gen.get_password(
mp['ADMIN_PASSWORD'] = pw_gen.get_password('horizon_keystone_admin', length=20) "service_token",
mp['SERVICE_PASSWORD'] = pw_gen.get_password('service_password') 'the service admin token',
)
mp['ADMIN_PASSWORD'] = pw_gen.get_password(
'horizon_keystone_admin',
'the horizon and keystone admin',
length=20,
)
mp['SERVICE_PASSWORD'] = pw_gen.get_password(
'service_password',
'service authentication',
)
#components of the auth endpoint #components of the auth endpoint
keystone_auth_host = config.getdefaulted('keystone', 'keystone_auth_host', host_ip) keystone_auth_host = config.getdefaulted('keystone', 'keystone_auth_host', host_ip)

View File

@ -17,7 +17,6 @@
import io import io
from devstack import cfg from devstack import cfg
from devstack import cfg_helpers
from devstack import component as comp from devstack import component as comp
from devstack import log as logging from devstack import log as logging
from devstack import settings from devstack import settings
@ -110,7 +109,7 @@ class MelangeInstaller(comp.PythonInstallComponent):
with io.BytesIO(contents) as stream: with io.BytesIO(contents) as stream:
config = cfg.IgnoreMissingConfigParser() config = cfg.IgnoreMissingConfigParser()
config.readfp(stream) config.readfp(stream)
db_dsn = cfg_helpers.fetch_dbdsn(self.cfg, self.pw_gen, DB_NAME) db_dsn = db.fetch_dbdsn(self.cfg, self.pw_gen, DB_NAME)
old_dbsn = config.get('DEFAULT', 'sql_connection') old_dbsn = config.get('DEFAULT', 'sql_connection')
if db_dsn != old_dbsn: if db_dsn != old_dbsn:
config.set('DEFAULT', 'sql_connection', db_dsn) config.set('DEFAULT', 'sql_connection', db_dsn)

View File

@ -16,7 +16,6 @@
from urlparse import urlunparse from urlparse import urlunparse
from devstack import cfg_helpers
from devstack import component as comp from devstack import component as comp
from devstack import date from devstack import date
from devstack import exceptions from devstack import exceptions
@ -610,7 +609,7 @@ class NovaConfConfigurator(object):
nova_conf.add('my_ip', hostip) nova_conf.add('my_ip', hostip)
#setup your sql connection #setup your sql connection
db_dsn = cfg_helpers.fetch_dbdsn(self.cfg, self.pw_gen, DB_NAME) db_dsn = db.fetch_dbdsn(self.cfg, self.pw_gen, DB_NAME)
nova_conf.add('sql_connection', db_dsn) nova_conf.add('sql_connection', db_dsn)
#configure anything libvirt releated? #configure anything libvirt releated?

View File

@ -17,7 +17,6 @@
import io import io
from devstack import cfg from devstack import cfg
from devstack import cfg_helpers
from devstack import component as comp from devstack import component as comp
from devstack import log as logging from devstack import log as logging
from devstack import settings from devstack import settings
@ -148,7 +147,7 @@ class QuantumInstaller(comp.PkgInstallComponent):
config.readfp(stream) config.readfp(stream)
db_dsn = config.get("DATABASE", "sql_connection") db_dsn = config.get("DATABASE", "sql_connection")
if db_dsn: if db_dsn:
generated_dsn = cfg_helpers.fetch_dbdsn(self.cfg, self.pw_gen, DB_NAME) generated_dsn = db.fetch_dbdsn(self.cfg, self.pw_gen, DB_NAME)
if generated_dsn != db_dsn: if generated_dsn != db_dsn:
config.set("DATABASE", "sql_connection", generated_dsn) config.set("DATABASE", "sql_connection", generated_dsn)
with io.BytesIO() as outputstream: with io.BytesIO() as outputstream:

View File

@ -23,17 +23,6 @@ import re
LOG = logging.getLogger("devstack.passwords") LOG = logging.getLogger("devstack.passwords")
PW_SECTION = 'passwords' PW_SECTION = 'passwords'
HELPFUL_DESCRIPTIONS = {
'sql': 'the database user',
'rabbit': 'the rabbit user',
'horizon_keystone_admin': 'the horizon and keystone admin',
'service_password': 'service authentication',
"service_token": 'the service admin token',
}
def get_pw_usage(option):
return HELPFUL_DESCRIPTIONS.get(option, '???')
def generate_random(length): def generate_random(length):
@ -78,12 +67,8 @@ class PasswordGenerator(object):
def _set_through(self, option, value): def _set_through(self, option, value):
self.cfg.set(PW_SECTION, option, value) self.cfg.set(PW_SECTION, option, value)
def get_password(self, option, prompt_text=None, length=8): def get_password(self, option, prompt_text, length=8):
"""Returns a password identified by the configuration location.""" """Returns a password identified by the configuration location."""
if not prompt_text:
prompt_text = get_pw_usage(option)
LOG.debug('Looking for password %s (%s)', option, prompt_text) LOG.debug('Looking for password %s (%s)', option, prompt_text)
# Look in the configuration file(s) # Look in the configuration file(s)

49
tests/test_db.py Normal file
View File

@ -0,0 +1,49 @@
from ConfigParser import ConfigParser
import mox
from devstack.components import db
from devstack import passwords
def test_fetch_dbdsn_full():
cfg = ConfigParser()
cfg.add_section('db')
cfg.set('db', 'sql_user', 'sql_user')
cfg.set('db', 'sql_host', 'sql_host')
cfg.set('db', 'port', '55')
cfg.set('db', 'type', 'mysql')
cfg.add_section('passwords')
cfg.set('passwords', 'sql', 'password')
dsn = db.fetch_dbdsn(cfg, passwords.PasswordGenerator(cfg, False))
assert dsn == 'mysql://sql_user:password@sql_host:55/'
def test_fetch_dbdsn_no_user():
cfg = ConfigParser()
cfg.add_section('db')
cfg.set('db', 'sql_user', '')
cfg.set('db', 'sql_host', 'sql_host')
cfg.set('db', 'port', '55')
cfg.set('db', 'type', 'mysql')
cfg.add_section('passwords')
cfg.set('passwords', 'sql', 'password')
dsn = db.fetch_dbdsn(cfg, passwords.PasswordGenerator(cfg, False))
assert dsn == 'mysql://:password@sql_host:55/'
def test_fetch_dbdsn_dbname():
cfg = ConfigParser()
cfg.add_section('db')
cfg.set('db', 'sql_user', 'sql_user')
cfg.set('db', 'sql_host', 'sql_host')
cfg.set('db', 'port', '55')
cfg.set('db', 'type', 'mysql')
cfg.add_section('passwords')
cfg.set('passwords', 'sql', 'password')
pw_gen = passwords.PasswordGenerator(cfg, False)
dsn = db.fetch_dbdsn(cfg, pw_gen, 'dbname')
assert dsn == 'mysql://sql_user:password@sql_host:55/dbname'