diff --git a/devstack/cfg_helpers.py b/devstack/cfg_helpers.py index cd64cb61..be93f876 100644 --- a/devstack/cfg_helpers.py +++ b/devstack/cfg_helpers.py @@ -14,7 +14,6 @@ # License for the specific language governing permissions and limitations # under the License. -from devstack import exceptions as excp from devstack import log as logging from devstack import settings @@ -31,39 +30,7 @@ def make_id(section, option): 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() 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 = "://:@:/" - 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 diff --git a/devstack/components/db.py b/devstack/components/db.py index 93929392..7c9defcf 100644 --- a/devstack/components/db.py +++ b/devstack/components/db.py @@ -22,6 +22,7 @@ from devstack import shell as sh from devstack import utils #id +# FIXME: This should probably come from the persona TYPE = settings.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]' #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): @@ -52,8 +54,8 @@ class DBUninstaller(comp.PkgUninstallComponent): self.runtime = DBRuntime(*args, **kargs) def warm_configs(self): - for pw_key in WARMUP_PWS: - self.pw_gen.get_password(pw_key) + for key, prompt in WARMUP_PWS: + self.pw_gen.get_password(key, prompt) def pre_uninstall(self): 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.") self.runtime.restart() params = { - 'OLD_PASSWORD': self.pw_gen.get_password('sql'), + 'OLD_PASSWORD': self.pw_gen.get_password('sql', PASSWORD_PROMPT), 'NEW_PASSWORD': RESET_BASE_PW, 'USER': self.cfg.getdefaulted("db", "sql_user", 'root'), } @@ -89,7 +91,7 @@ class DBInstaller(comp.PkgInstallComponent): #in pre-install and post-install sections host_ip = self.cfg.get('host', 'ip') out = { - 'PASSWORD': self.pw_gen.get_password("sql"), + 'PASSWORD': self.pw_gen.get_password("sql", PASSWORD_PROMPT), 'BOOT_START': ("%s" % (True)).lower(), 'USER': self.cfg.getdefaulted("db", "sql_user", 'root'), 'SERVICE_HOST': host_ip, @@ -98,8 +100,8 @@ class DBInstaller(comp.PkgInstallComponent): return out def warm_configs(self): - for pw_key in WARMUP_PWS: - self.pw_gen.get_password(pw_key) + for key, prompt in WARMUP_PWS: + self.pw_gen.get_password(key, prompt) def _configure_db_confs(self): 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.") self.runtime.restart() 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'), '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.") self.runtime.restart() params = { - 'PASSWORD': self.pw_gen.get_password("sql"), + 'PASSWORD': self.pw_gen.get_password("sql", PASSWORD_PROMPT), 'USER': user, } cmds = [{'cmd': grant_cmd}] @@ -243,7 +245,7 @@ def drop_db(cfg, pw_gen, distro, dbname): if dbactions and dbactions.get('drop_db'): dropcmd = dbactions.get('drop_db') 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['DB'] = dbname cmds = list() @@ -263,7 +265,7 @@ def create_db(cfg, pw_gen, distro, dbname): if dbactions and dbactions.get('create_db'): createcmd = dbactions.get('create_db') 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['DB'] = dbname cmds = list() @@ -275,3 +277,36 @@ def create_db(cfg, pw_gen, distro, dbname): else: msg = BASE_ERROR % ('create', dbtype) 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 = "://:@:/" + 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 diff --git a/devstack/components/glance.py b/devstack/components/glance.py index 4b532ba2..65e11b54 100644 --- a/devstack/components/glance.py +++ b/devstack/components/glance.py @@ -17,7 +17,6 @@ import io from devstack import cfg -from devstack import cfg_helpers from devstack import component as comp from devstack import log as logging from devstack import settings @@ -173,7 +172,7 @@ class GlanceInstaller(comp.PythonInstallComponent): mp = dict() mp['DEST'] = self.appdir 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['HOST_IP'] = self.cfg.get('host', 'ip') mp.update(keystone.get_shared_params(self.cfg, self.pw_gen, 'glance')) diff --git a/devstack/components/keystone.py b/devstack/components/keystone.py index 2734931f..b224496d 100644 --- a/devstack/components/keystone.py +++ b/devstack/components/keystone.py @@ -19,7 +19,6 @@ import io from urlparse import urlunparse from devstack import cfg -from devstack import cfg_helpers from devstack import component as comp from devstack import log as logging from devstack import settings @@ -192,7 +191,7 @@ class KeystoneInstaller(comp.PythonInstallComponent): mp['BIN_DIR'] = self.bindir mp['CONFIG_FILE'] = sh.joinpths(self.cfgdir, 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.update(get_shared_params(self.cfg, self.pw_gen)) 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'] #tokens and passwords - mp['SERVICE_TOKEN'] = pw_gen.get_password("service_token") - mp['ADMIN_PASSWORD'] = pw_gen.get_password('horizon_keystone_admin', length=20) - mp['SERVICE_PASSWORD'] = pw_gen.get_password('service_password') + mp['SERVICE_TOKEN'] = pw_gen.get_password( + "service_token", + '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 keystone_auth_host = config.getdefaulted('keystone', 'keystone_auth_host', host_ip) diff --git a/devstack/components/melange.py b/devstack/components/melange.py index 7d6c5f6e..42171e7e 100644 --- a/devstack/components/melange.py +++ b/devstack/components/melange.py @@ -17,7 +17,6 @@ import io from devstack import cfg -from devstack import cfg_helpers from devstack import component as comp from devstack import log as logging from devstack import settings @@ -110,7 +109,7 @@ class MelangeInstaller(comp.PythonInstallComponent): with io.BytesIO(contents) as stream: config = cfg.IgnoreMissingConfigParser() 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') if db_dsn != old_dbsn: config.set('DEFAULT', 'sql_connection', db_dsn) diff --git a/devstack/components/nova.py b/devstack/components/nova.py index e36e6068..33ff05e1 100644 --- a/devstack/components/nova.py +++ b/devstack/components/nova.py @@ -16,7 +16,6 @@ from urlparse import urlunparse -from devstack import cfg_helpers from devstack import component as comp from devstack import date from devstack import exceptions @@ -610,7 +609,7 @@ class NovaConfConfigurator(object): nova_conf.add('my_ip', hostip) #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) #configure anything libvirt releated? diff --git a/devstack/components/quantum.py b/devstack/components/quantum.py index 314c998d..ee51c0e8 100644 --- a/devstack/components/quantum.py +++ b/devstack/components/quantum.py @@ -17,7 +17,6 @@ import io from devstack import cfg -from devstack import cfg_helpers from devstack import component as comp from devstack import log as logging from devstack import settings @@ -148,7 +147,7 @@ class QuantumInstaller(comp.PkgInstallComponent): config.readfp(stream) db_dsn = config.get("DATABASE", "sql_connection") 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: config.set("DATABASE", "sql_connection", generated_dsn) with io.BytesIO() as outputstream: diff --git a/devstack/passwords.py b/devstack/passwords.py index 83ae90fe..d50a8f59 100644 --- a/devstack/passwords.py +++ b/devstack/passwords.py @@ -23,17 +23,6 @@ import re LOG = logging.getLogger("devstack.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): @@ -78,12 +67,8 @@ class PasswordGenerator(object): def _set_through(self, 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.""" - - if not prompt_text: - prompt_text = get_pw_usage(option) - LOG.debug('Looking for password %s (%s)', option, prompt_text) # Look in the configuration file(s) diff --git a/tests/test_db.py b/tests/test_db.py new file mode 100644 index 00000000..3a94544c --- /dev/null +++ b/tests/test_db.py @@ -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'