Got quantum install + configuration working.

This commit is contained in:
Joshua Harlow
2012-01-25 17:30:40 -08:00
parent 3bb3e275bf
commit bc8d51c1db
10 changed files with 198 additions and 43 deletions

View File

@@ -140,7 +140,7 @@ class PkgInstallComponent(ComponentBase):
#ensure directory is there (if not created previously) #ensure directory is there (if not created previously)
self.tracewriter.make_dir(sh.dirname(tgtfn)) self.tracewriter.make_dir(sh.dirname(tgtfn))
#now configure it #now configure it
LOG.info("Configuring template file %s" % (fn)) LOG.info("Configuring file %s" % (fn))
(sourcefn, contents) = self._get_source_config(fn) (sourcefn, contents) = self._get_source_config(fn)
LOG.info("Replacing parameters in file %s" % (sourcefn)) LOG.info("Replacing parameters in file %s" % (sourcefn))
LOG.debug("Replacements = %s" % (parameters)) LOG.debug("Replacements = %s" % (parameters))

View File

@@ -56,6 +56,7 @@ BASE_ERROR = 'Currently we do not know how to %s for database type [%s]'
#used to make params for booting when started (not always take advantage of...) #used to make params for booting when started (not always take advantage of...)
BOOLEAN_OUTPUT = {True: 'true', False: 'false'} BOOLEAN_OUTPUT = {True: 'true', False: 'false'}
class DBUninstaller(comp.PkgUninstallComponent): class DBUninstaller(comp.PkgUninstallComponent):
def __init__(self, *args, **kargs): def __init__(self, *args, **kargs):
comp.PkgUninstallComponent.__init__(self, TYPE, *args, **kargs) comp.PkgUninstallComponent.__init__(self, TYPE, *args, **kargs)

View File

@@ -109,6 +109,7 @@ class GlanceInstaller(comp.PythonInstallComponent):
return parent_result return parent_result
def _setup_db(self): def _setup_db(self):
LOG.info("Fixing up database named %s", DB_NAME)
db.drop_db(self.cfg, DB_NAME) db.drop_db(self.cfg, DB_NAME)
db.create_db(self.cfg, DB_NAME) db.create_db(self.cfg, DB_NAME)

View File

@@ -85,6 +85,7 @@ class KeystoneInstaller(comp.PythonInstallComponent):
return list(CONFIGS) return list(CONFIGS)
def _setup_db(self): def _setup_db(self):
LOG.info("Fixing up database named %s", DB_NAME)
db.drop_db(self.cfg, DB_NAME) db.drop_db(self.cfg, DB_NAME)
db.create_db(self.cfg, DB_NAME) db.create_db(self.cfg, DB_NAME)

View File

@@ -13,47 +13,186 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import io
from devstack import cfg
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
from devstack import shell as sh from devstack import shell as sh
from devstack import utils from devstack import utils
from devstack.components import db
LOG = logging.getLogger("devstack.components.quantum") LOG = logging.getLogger("devstack.components.quantum")
#vswitch pkgs
VSWITCH_PLUGIN = 'openvswitch'
PKG_VSWITCH = "quantum-openvswitch.json"
V_PROVIDER = "quantum.plugins.openvswitch.ovs_quantum_plugin.OVSQuantumPlugin"
#config files (some only modified if running as openvswitch
PLUGIN_CONF = "plugins.ini"
PLUGIN_LOC = ['etc']
AGENT_CONF = 'ovs_quantum_plugin.ini'
AGENT_LOC = ["etc", "quantum", "plugins", "openvswitch"]
CONFIG_FILES = [PLUGIN_CONF, AGENT_CONF]
#this db will be dropped and created
DB_NAME = 'ovs_quantum'
#opensvswitch bridge setup/teardown/name commands
OVS_BRIDGE_DEL = ['ovs-vsctl', '--no-wait', '--', '--if-exists', 'del-br', '%OVS_BRIDGE%']
OVS_BRIDGE_ADD = ['ovs-vsctl', '--no-wait', 'add-br', '%OVS_BRIDGE%']
OVS_BRIDGE_EXTERN_ID = ['ovs-vsctl', '--no-wait', 'br-set-external-id', '%OVS_BRIDGE%', 'bridge-id', '%OVS_EXTERNAL_ID%']
#id #id
TYPE = settings.QUANTUM TYPE = settings.QUANTUM
#special component options
QUANTUM_SERVICE = 'q-svc'
QUANTUM_AGENT = 'q-agt'
class QuantumUninstaller(object):
class QuantumUninstaller(comp.PkgUninstallComponent):
def __init__(self, *args, **kargs): def __init__(self, *args, **kargs):
pass comp.PkgUninstallComponent.__init__(self, TYPE, *args, **kargs)
def unconfigure(self):
raise NotImplementedError()
def uninstall(self):
raise NotImplementedError()
class QuantumInstaller(object): class QuantumInstaller(comp.PkgInstallComponent):
def __init__(self, *args, **kargs): def __init__(self, *args, **kargs):
pass comp.PkgInstallComponent.__init__(self, TYPE, *args, **kargs)
self.git_loc = self.cfg.get("git", "quantum_repo")
self.git_branch = self.cfg.get("git", "quantum_branch")
self.q_vswitch_agent = False
self.q_vswitch_service = False
plugin = self.cfg.get("quantum", "q_plugin")
if plugin == VSWITCH_PLUGIN:
if len(self.component_opts) == 0:
#default to on if not specified
self.q_vswitch_agent = True
self.q_vswitch_service = True
else:
#only turn on if requested
if QUANTUM_SERVICE in self.component_opts:
self.q_vswitch_service = True
if QUANTUM_AGENT in self.component_opts:
self.q_vswitch_agent = True
def download(self): def _get_download_locations(self):
raise NotImplementedError() places = comp.PkgInstallComponent._get_download_locations(self)
places.append({
'uri': self.git_loc,
'branch': self.git_branch,
})
return places
def configure(self): def get_pkglist(self):
raise NotImplementedError() if self.q_vswitch_service:
listing_fn = sh.joinpths(settings.STACK_PKG_DIR, PKG_VSWITCH)
return utils.extract_pkg_list([listing_fn], self.distro)
else:
return comp.PkgInstallComponent.get_pkglist(self)
def pre_install(self): def _get_config_files(self):
raise NotImplementedError() parent_list = comp.PkgInstallComponent._get_config_files(self)
parent_list.extend(CONFIG_FILES)
return parent_list
def install(self): def _get_target_config_name(self, config_fn):
raise NotImplementedError() if config_fn == PLUGIN_CONF:
tgt_loc = [self.appdir] + PLUGIN_LOC + [config_fn]
return sh.joinpths(*tgt_loc)
elif config_fn == AGENT_CONF:
tgt_loc = [self.appdir] + AGENT_LOC + [config_fn]
return sh.joinpths(*tgt_loc)
else:
return comp.PkgInstallComponent._get_target_config_name(self, config_fn)
def _config_adjust(self, contents, config_fn):
if config_fn == PLUGIN_CONF and self.q_vswitch_service:
#need to fix the "Quantum plugin provider module"
newcontents = contents
with io.BytesIO(contents) as stream:
config = cfg.IgnoreMissingConfigParser()
config.readfp(stream)
provider = config.get("PLUGIN", "provider")
if provider and provider != V_PROVIDER:
config.set("PLUGIN", "provider", V_PROVIDER)
with io.BytesIO() as outputstream:
config.write(outputstream)
outputstream.flush()
#TODO can we write to contents here directly?
newcontents = outputstream.getvalue()
return newcontents
elif config_fn == AGENT_CONF and self.q_vswitch_agent:
#Need to adjust the sql connection
newcontents = contents
with io.BytesIO(contents) as stream:
config = cfg.IgnoreMissingConfigParser()
config.readfp(stream)
db_dsn = config.get("DATABASE", "sql_connection")
if db_dsn:
generated_dsn = self.cfg.get_dbdsn(DB_NAME)
if generated_dsn != db_dsn:
config.set("DATABASE", "sql_connection", generated_dsn)
with io.BytesIO() as outputstream:
config.write(outputstream)
outputstream.flush()
#TODO can we write to contents here directly?
newcontents = outputstream.getvalue()
return newcontents
else:
return comp.PkgInstallComponent._config_adjust(self, contents, config_fn)
def _setup_bridge(self):
bridge = self.cfg.get("quantum", "ovs_bridge")
if bridge:
LOG.info("Fixing up ovs bridge named %s", bridge)
external_id = self.cfg.get("quantum", 'ovs_bridge_external_name')
if not external_id:
external_id = bridge
params = dict()
params['OVS_BRIDGE'] = bridge
params['OVS_EXTERNAL_ID'] = external_id
cmds = list()
cmds.append({
'cmd': OVS_BRIDGE_DEL
})
cmds.append({
'cmd': OVS_BRIDGE_ADD
})
cmds.append({
'cmd': OVS_BRIDGE_EXTERN_ID
})
utils.execute_template(*cmds, params=params)
#TODO maybe have a trace that says we did this so that we can remove it on uninstall?
def post_install(self): def post_install(self):
raise NotImplementedError() parent_result = comp.PkgInstallComponent.post_install(self)
if self.q_vswitch_service and settings.DB in self.instances:
self._setup_db()
if self.q_vswitch_agent:
self._setup_bridge()
return parent_result
def _setup_db(self):
LOG.info("Fixing up database named %s", DB_NAME)
db.drop_db(self.cfg, DB_NAME)
db.create_db(self.cfg, DB_NAME)
def _get_source_config(self, config_fn):
if config_fn == PLUGIN_CONF:
srcloc = [self.appdir] + PLUGIN_LOC + [config_fn]
srcfn = sh.joinpths(*srcloc)
contents = sh.load_file(srcfn)
return (srcfn, contents)
elif config_fn == AGENT_CONF:
srcloc = [self.appdir] + AGENT_LOC + [config_fn]
srcfn = sh.joinpths(*srcloc)
contents = sh.load_file(srcfn)
return (srcfn, contents)
else:
return comp.PkgInstallComponent._get_source_config(self, config_fn)
class QuantumRuntime(comp.EmptyRuntime): class QuantumRuntime(comp.EmptyRuntime):

View File

@@ -46,5 +46,5 @@ def download(storewhere, uri, branch=None):
EXT_REG.match(up.path): EXT_REG.match(up.path):
return _gitdownload(storewhere, uri, branch) return _gitdownload(storewhere, uri, branch)
else: else:
msg = "Currently we do not know how to download %s" % (uri) msg = "Currently we do not know how to download from uri [%s]" % (uri)
raise NotImplementedError(msg) raise NotImplementedError(msg)

View File

@@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
class StackException(Exception): class StackException(Exception):
pass pass

View File

@@ -100,7 +100,9 @@ COMPONENT_DEPENDENCIES = {
SWIFT: [], SWIFT: [],
NOVA_CLIENT: [], NOVA_CLIENT: [],
HORIZON: [KEYSTONE_CLIENT, GLANCE, NOVA_CLIENT, OPENSTACK_X], HORIZON: [KEYSTONE_CLIENT, GLANCE, NOVA_CLIENT, OPENSTACK_X],
QUANTUM: [], #the db isn't always a dependency (depending on the quantum component to be activated)
#for now assume it is (TODO make it better?)
QUANTUM: [DB],
NOVNC: [], NOVNC: [],
} }
@@ -200,7 +202,8 @@ PKG_MAP = {
], ],
QUANTUM: QUANTUM:
[ [
os.path.join(STACK_PKG_DIR, "quantum.json"), #quantum figures out its own pkgs
#they will be listed in the quantum component
], ],
DB: DB:
[ [

View File

@@ -135,17 +135,12 @@ def determine_distro():
return (found_os, plt) return (found_os, plt)
def get_pip_list(distro, component): def extract_pip_list(fns, distro):
LOG.info("Getting pip packages for distro %s and component %s." % (distro, component))
all_pkgs = dict() all_pkgs = dict()
fns = settings.PIP_MAP.get(component)
if fns is None:
return all_pkgs
#load + merge them
for fn in fns: for fn in fns:
js = load_json(fn) js = load_json(fn)
distro_pkgs = js.get(distro) distro_pkgs = js.get(distro)
if distro_pkgs and len(distro_pkgs): if distro_pkgs:
combined = dict(all_pkgs) combined = dict(all_pkgs)
for (pkgname, pkginfo) in distro_pkgs.items(): for (pkgname, pkginfo) in distro_pkgs.items():
#we currently just overwrite #we currently just overwrite
@@ -154,17 +149,21 @@ def get_pip_list(distro, component):
return all_pkgs return all_pkgs
def get_pkg_list(distro, component): def get_pip_list(distro, component):
LOG.info("Getting packages for distro %s and component %s." % (distro, component)) LOG.info("Getting pip packages for distro %s and component %s." % (distro, component))
all_pkgs = dict() fns = settings.PIP_MAP.get(component)
fns = settings.PKG_MAP.get(component)
if fns is None: if fns is None:
return all_pkgs return dict()
#load + merge them else:
return extract_pip_list(fns, distro)
def extract_pkg_list(fns, distro):
all_pkgs = dict()
for fn in fns: for fn in fns:
js = load_json(fn) js = load_json(fn)
distro_pkgs = js.get(distro) distro_pkgs = js.get(distro)
if distro_pkgs and len(distro_pkgs): if distro_pkgs:
combined = dict(all_pkgs) combined = dict(all_pkgs)
for (pkgname, pkginfo) in distro_pkgs.items(): for (pkgname, pkginfo) in distro_pkgs.items():
if pkgname in all_pkgs.keys(): if pkgname in all_pkgs.keys():
@@ -173,7 +172,8 @@ def get_pkg_list(distro, component):
for (infokey, infovalue) in pkginfo.items(): for (infokey, infovalue) in pkginfo.items():
#this is expected to be a list of cmd actions #this is expected to be a list of cmd actions
#so merge that accordingly #so merge that accordingly
if infokey == settings.PRE_INSTALL or infokey == settings.POST_INSTALL: if(infokey == settings.PRE_INSTALL or
infokey == settings.POST_INSTALL):
oldinstalllist = oldpkginfo.get(infokey) or [] oldinstalllist = oldpkginfo.get(infokey) or []
infovalue = oldinstalllist + infovalue infovalue = oldinstalllist + infovalue
newpkginfo[infokey] = infovalue newpkginfo[infokey] = infovalue
@@ -184,6 +184,15 @@ def get_pkg_list(distro, component):
return all_pkgs return all_pkgs
def get_pkg_list(distro, component):
LOG.info("Getting packages for distro %s and component %s." % (distro, component))
fns = settings.PKG_MAP.get(component)
if fns is None:
return dict()
else:
return extract_pkg_list(fns, distro)
def joinlinesep(*pieces): def joinlinesep(*pieces):
return os.linesep.join(pieces) return os.linesep.join(pieces)