Large update to re juggle how dependencies are resolved.

This time not using priorities but a true topo sort.
Also removed pkg listings and pip listings from settings and moved them to there individual components.
Split up the 2 programs, one for listing deps and one for describing components into 2 programs.
This commit is contained in:
Joshua Harlow 2012-01-28 21:05:54 -08:00
parent b6c22ea16e
commit 8fd53948df
22 changed files with 602 additions and 474 deletions

@ -43,15 +43,20 @@ class ComponentBase(object):
self.cfg = kargs.get("config")
self.packager = kargs.get("packager")
self.distro = kargs.get("distro")
self.component_name = component_name
self.instances = kargs.get("instances", set())
self.component_opts = kargs.get('opts', list())
self.root = kargs.get("root")
self.instances = kargs.get("instances")
self.component_opts = kargs.get('opts')
self.component_root = sh.joinpths(self.root, component_name)
self.tracedir = sh.joinpths(self.component_root, settings.COMPONENT_TRACE_DIR)
self.appdir = sh.joinpths(self.component_root, settings.COMPONENT_APP_DIR)
self.cfgdir = sh.joinpths(self.component_root, settings.COMPONENT_CONFIG_DIR)
self.component_name = component_name
def get_dependencies(self):
deps = settings.COMPONENT_DEPENDENCIES.get(self.component_name)
if not deps:
return list()
return list(deps)
class PkgInstallComponent(ComponentBase):
def __init__(self, component_name, *args, **kargs):
@ -85,11 +90,11 @@ class PkgInstallComponent(ComponentBase):
def _get_param_map(self, config_fn):
return None
def _get_pkglist(self):
return utils.get_pkg_list(self.distro, self.component_name)
def _get_pkgs(self):
return dict()
def install(self):
pkgs = self._get_pkglist()
pkgs = self._get_pkgs()
if pkgs:
pkgnames = sorted(pkgs.keys())
LOG.info("Installing packages (%s)." % (", ".join(pkgnames)))
@ -100,14 +105,14 @@ class PkgInstallComponent(ComponentBase):
return self.tracedir
def pre_install(self):
pkgs = utils.get_pkg_list(self.distro, self.component_name)
pkgs = self._get_pkgs()
if pkgs:
mp = self._get_param_map(None)
self.packager.pre_install(pkgs, mp)
return self.tracedir
def post_install(self):
pkgs = utils.get_pkg_list(self.distro, self.component_name)
pkgs = self._get_pkgs()
if pkgs:
mp = self._get_param_map(None)
self.packager.post_install(pkgs, mp)
@ -167,12 +172,12 @@ class PythonInstallComponent(PkgInstallComponent):
})
return py_dirs
def _get_pip_list(self):
return utils.get_pip_list(self.distro, self.component_name)
def _get_pips(self):
return dict()
def _install_pips(self):
#install any need pip items
pips = self._get_pip_list()
pips = self._get_pips()
if pips:
LOG.info("Setting up %s pips (%s)" % (len(pips), ", ".join(pips.keys())))
pip.install(pips)

@ -15,14 +15,13 @@
# under the License.
from devstack import component as comp
from devstack import settings
from devstack import exceptions as excp
from devstack import log as logging
from devstack import settings
from devstack import shell as sh
from devstack import trace as tr
from devstack import utils
LOG = logging.getLogger("devstack.components.db")
#id
@ -57,6 +56,9 @@ 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...)
BOOLEAN_OUTPUT = {True: 'true', False: 'false'}
#the pkg json files db requires for installation
REQ_PKGS = ['db.json']
class DBUninstaller(comp.PkgUninstallComponent):
def __init__(self, *args, **kargs):
@ -80,6 +82,13 @@ class DBInstaller(comp.PkgInstallComponent):
out['HOST_IP'] = host_ip
return out
def _get_pkgs(self):
pkgs = comp.PkgInstallComponent._get_pkgs(self)
for fn in REQ_PKGS:
full_name = sh.joinpths(settings.STACK_PKG_DIR, fn)
pkgs = utils.extract_pkg_list([full_name], self.distro, pkgs)
return pkgs
def post_install(self):
parent_result = comp.PkgInstallComponent.post_install(self)
#extra actions to ensure we are granted access

@ -21,8 +21,11 @@ from devstack import component as comp
from devstack import log as logging
from devstack import settings
from devstack import shell as sh
from devstack import utils
from devstack.components import db
from devstack.components import keystone
from devstack.image import creator
LOG = logging.getLogger("devstack.components.glance")
@ -64,6 +67,9 @@ SUB_TO_APP = {
CONFIG_DIR = 'etc'
BIN_DIR = 'bin'
#the pkg json files glance requires for installation
REQ_PKGS = ['general.json', 'glance.json']
class GlanceUninstaller(comp.PythonUninstallComponent):
def __init__(self, *args, **kargs):
@ -71,39 +77,6 @@ class GlanceUninstaller(comp.PythonUninstallComponent):
self.cfgdir = sh.joinpths(self.appdir, CONFIG_DIR)
class GlanceRuntime(comp.PythonRuntime):
def __init__(self, *args, **kargs):
comp.PythonRuntime.__init__(self, TYPE, *args, **kargs)
self.cfgdir = sh.joinpths(self.appdir, CONFIG_DIR)
def _get_apps_to_start(self):
apps = list()
if not self.component_opts:
for app_name in APP_OPTIONS.keys():
apps.append({
'name': app_name,
'path': sh.joinpths(self.appdir, BIN_DIR, app_name),
})
else:
for short_name in self.component_opts:
full_name = SUB_TO_APP.get(short_name)
if full_name and full_name in APP_OPTIONS:
apps.append({
'name': full_name,
'path': sh.joinpths(self.appdir, BIN_DIR, full_name),
})
return apps
def _get_app_options(self, app):
return APP_OPTIONS.get(app)
def post_start(self):
comp.PythonRuntime.post_start(self)
if NO_IMG_START not in self.component_opts:
#install any images that need activating...
creator.ImageCreationService(self.cfg).install()
class GlanceInstaller(comp.PythonInstallComponent):
def __init__(self, *args, **kargs):
comp.PythonInstallComponent.__init__(self, TYPE, *args, **kargs)
@ -123,6 +96,13 @@ class GlanceInstaller(comp.PythonInstallComponent):
#these are the config files we will be adjusting
return list(CONFIGS)
def _get_pkgs(self):
pkgs = comp.PythonInstallComponent._get_pkgs(self)
for fn in REQ_PKGS:
full_name = sh.joinpths(settings.STACK_PKG_DIR, fn)
pkgs = utils.extract_pkg_list([full_name], self.distro, pkgs)
return pkgs
def post_install(self):
parent_result = comp.PythonInstallComponent.post_install(self)
self._setup_db()
@ -193,6 +173,39 @@ class GlanceInstaller(comp.PythonInstallComponent):
return mp
class GlanceRuntime(comp.PythonRuntime):
def __init__(self, *args, **kargs):
comp.PythonRuntime.__init__(self, TYPE, *args, **kargs)
self.cfgdir = sh.joinpths(self.appdir, CONFIG_DIR)
def _get_apps_to_start(self):
apps = list()
if not self.component_opts:
for app_name in APP_OPTIONS.keys():
apps.append({
'name': app_name,
'path': sh.joinpths(self.appdir, BIN_DIR, app_name),
})
else:
for short_name in self.component_opts:
full_name = SUB_TO_APP.get(short_name)
if full_name and full_name in APP_OPTIONS:
apps.append({
'name': full_name,
'path': sh.joinpths(self.appdir, BIN_DIR, full_name),
})
return apps
def _get_app_options(self, app):
return APP_OPTIONS.get(app)
def post_start(self):
comp.PythonRuntime.post_start(self)
if NO_IMG_START not in self.component_opts:
#install any images that need activating...
creator.ImageCreationService(self.cfg).install()
def describe(opts=None):
description = """
Module: {module_name}
@ -203,9 +216,12 @@ def describe(opts=None):
"""
copts = """
{no_img_upload} - disables upload of test images to glance.
{glance_api} - only enable the glance api subcomponent.
{glance_reg} - only enable the glance registry subcomponent.
"""
params = dict()
params['component_opts'] = copts.strip("\n").format(no_img_upload=NO_IMG_START)
params['component_opts'] = copts.strip("\n").format(no_img_upload=NO_IMG_START,
glance_api=GAPI, glance_reg=GREG)
params['module_name'] = __name__
params['description'] = __doc__ or "Handles actions for the glance component."
out = description.format(**params)

@ -20,6 +20,7 @@ from devstack import component as comp
from devstack import log as logging
from devstack import settings
from devstack import shell as sh
from devstack import utils
#id
TYPE = settings.HORIZON
@ -54,6 +55,12 @@ BAD_APACHE_USERS = ['root']
LOG = logging.getLogger("devstack.components.horizon")
#the pkg json files horizon requires for installation
REQ_PKGS = ['general.json', 'horizon.json']
#pip files that horizon requires
REQ_PIPS = ['horizon.json']
class HorizonUninstaller(comp.PythonUninstallComponent):
def __init__(self, *args, **kargs):
@ -76,6 +83,20 @@ class HorizonInstaller(comp.PythonInstallComponent):
})
return places
def _get_pkgs(self):
pkgs = comp.PythonInstallComponent._get_pkgs(self)
for fn in REQ_PKGS:
full_name = sh.joinpths(settings.STACK_PKG_DIR, fn)
pkgs = utils.extract_pkg_list([full_name], self.distro, pkgs)
return pkgs
def _get_pips(self):
pips = comp.PythonInstallComponent._get_pips(self)
for fn in REQ_PIPS:
full_name = sh.joinpths(settings.STACK_PIP_DIR, fn)
pips = utils.extract_pip_list([full_name], self.distro, pips)
return pips
def _get_target_config_name(self, config_name):
if config_name == HORIZON_PY_CONF:
return sh.joinpths(self.dash_dir, *HORIZON_PY_CONF_TGT)

@ -22,6 +22,7 @@ from devstack import log as logging
from devstack import settings
from devstack import shell as sh
from devstack import utils
from devstack.components import db
LOG = logging.getLogger("devstack.components.keystone")
@ -55,6 +56,12 @@ APP_OPTIONS = {
'--log-config=' + sh.joinpths('%ROOT%', CONFIG_DIR, 'logging.cnf')]
}
#the pkg json files keystone requires for installation
REQ_PKGS = ['general.json', 'keystone.json']
#pip files that horizon requires
REQ_PIPS = ['keystone.json']
class KeystoneUninstaller(comp.PythonUninstallComponent):
def __init__(self, *args, **kargs):
@ -79,6 +86,20 @@ class KeystoneInstaller(comp.PythonInstallComponent):
})
return places
def _get_pips(self):
pips = comp.PythonInstallComponent._get_pips(self)
for fn in REQ_PIPS:
full_name = sh.joinpths(settings.STACK_PIP_DIR, fn)
pips = utils.extract_pip_list([full_name], self.distro, pips)
return pips
def _get_pkgs(self):
pkgs = comp.PythonInstallComponent._get_pkgs(self)
for fn in REQ_PKGS:
full_name = sh.joinpths(settings.STACK_PKG_DIR, fn)
pkgs = utils.extract_pkg_list([full_name], self.distro, pkgs)
return pkgs
def post_install(self):
parent_result = comp.PythonInstallComponent.post_install(self)
self._setup_db()
@ -184,22 +205,22 @@ class KeystoneRuntime(comp.PythonRuntime):
return APP_OPTIONS.get(app)
def get_shared_params(cfg):
def get_shared_params(config):
mp = dict()
host_ip = cfg.get('host', 'ip')
keystone_auth_host = cfg.get('keystone', 'keystone_auth_host')
host_ip = config.get('host', 'ip')
keystone_auth_host = config.get('keystone', 'keystone_auth_host')
if not keystone_auth_host:
keystone_auth_host = host_ip
mp['KEYSTONE_AUTH_HOST'] = keystone_auth_host
mp['KEYSTONE_AUTH_PORT'] = cfg.get('keystone', 'keystone_auth_port')
mp['KEYSTONE_AUTH_PROTOCOL'] = cfg.get('keystone', 'keystone_auth_protocol')
keystone_service_host = cfg.get('keystone', 'keystone_service_host')
mp['KEYSTONE_AUTH_PORT'] = config.get('keystone', 'keystone_auth_port')
mp['KEYSTONE_AUTH_PROTOCOL'] = config.get('keystone', 'keystone_auth_protocol')
keystone_service_host = config.get('keystone', 'keystone_service_host')
if not keystone_service_host:
keystone_service_host = host_ip
mp['KEYSTONE_SERVICE_HOST'] = keystone_service_host
mp['KEYSTONE_SERVICE_PORT'] = cfg.get('keystone', 'keystone_service_port')
mp['KEYSTONE_SERVICE_PROTOCOL'] = cfg.get('keystone', 'keystone_service_protocol')
mp['SERVICE_TOKEN'] = cfg.get("passwords", "service_token")
mp['KEYSTONE_SERVICE_PORT'] = config.get('keystone', 'keystone_service_port')
mp['KEYSTONE_SERVICE_PROTOCOL'] = config.get('keystone', 'keystone_service_protocol')
mp['SERVICE_TOKEN'] = config.get("passwords", "service_token")
return mp

@ -17,12 +17,17 @@
from devstack import component as comp
from devstack import log as logging
from devstack import settings
from devstack import shell as sh
from devstack import utils
LOG = logging.getLogger("devstack.components.keystone_client")
#id
TYPE = settings.KEYSTONE_CLIENT
#the pkg json files keystone client requires for installation
REQ_PKGS = ['general.json', 'keystone-client.json']
class KeyStoneClientUninstaller(comp.PythonUninstallComponent):
def __init__(self, *args, **kargs):
@ -43,6 +48,13 @@ class KeyStoneClientInstaller(comp.PythonInstallComponent):
})
return places
def _get_pkgs(self):
pkgs = comp.PythonInstallComponent._get_pkgs(self)
for fn in REQ_PKGS:
full_name = sh.joinpths(settings.STACK_PKG_DIR, fn)
pkgs = utils.extract_pkg_list([full_name], self.distro, pkgs)
return pkgs
class KeyStoneClientRuntime(comp.EmptyRuntime):
def __init__(self, *args, **kargs):

@ -23,6 +23,7 @@ from devstack import log as logging
from devstack import settings
from devstack import shell as sh
from devstack import utils
from devstack.components import db
from devstack.components import keystone
@ -98,19 +99,23 @@ NCAUTH = "cauth"
SUBCOMPONENTS = [NCPU, NVOL, NAPI,
NOBJ, NNET, NCERT, NSCHED, NCAUTH]
#the pkg json files nova requires for installation
REQ_PKGS = ['general.json', 'nova.json']
# Additional packages for subcomponents
ADD_PKGS = {
NAPI:
[
sh.joinpths(settings.STACK_PKG_DIR, 'n-api.json'),
'n-api.json',
],
NCPU:
[
sh.joinpths(settings.STACK_PKG_DIR, 'n-cpu.json'),
'n-cpu.json',
],
NVOL:
[
sh.joinpths(settings.STACK_PKG_DIR, 'n-vol.json'),
'n-vol.json',
],
}
@ -181,22 +186,26 @@ class NovaInstaller(comp.PythonInstallComponent):
self.bindir = sh.joinpths(self.appdir, BIN_DIR)
self.paste_conf_fn = self._get_target_config_name(PASTE_CONF)
def _get_pkglist(self):
pkgs = comp.PythonInstallComponent._get_pkglist(self)
def _get_pkgs(self):
pkgs = comp.PythonInstallComponent._get_pkgs(self)
# Get the core pkgs
for fn in REQ_PKGS:
full_name = sh.joinpths(settings.STACK_PKG_DIR, fn)
pkgs = utils.extract_pkg_list([full_name], self.distro, pkgs)
# Walk through the subcomponents (like 'vol' and 'cpu') and add those
# those packages as well. Let utils.get_pkglist handle any missing
# entries
# those packages as well.
sub_components = []
if self.component_opts:
sub_components = self.component_opts
else:
# No subcomponents where explicitly specified, so get all
sub_components = SUBCOMPONENTS
LOG.debug("Explicit extras: %s" % (sub_components))
# Add the extra dependencies
for cname in sub_components:
subpkgsfns = ADD_PKGS.get(cname)
if subpkgsfns:
pkgs = utils.extract_pkg_list(subpkgsfns, self.distro, pkgs)
for c in sub_components:
fns = ADD_PKGS.get(c)
if fns:
for fn in fns:
full_name = sh.joinpths(settings.STACK_PKG_DIR, fn)
pkgs = utils.extract_pkg_list([full_name], self.distro, pkgs)
return pkgs
def _get_download_locations(self):

@ -17,12 +17,17 @@
from devstack import component as comp
from devstack import log as logging
from devstack import settings
from devstack import shell as sh
from devstack import utils
LOG = logging.getLogger("devstack.components.nova_client")
#id
TYPE = settings.NOVA_CLIENT
#the pkg json files nova client requires for installation
REQ_PKGS = ['general.json', 'nova-client.json']
class NovaClientUninstaller(comp.PythonUninstallComponent):
def __init__(self, *args, **kargs):
@ -43,6 +48,13 @@ class NovaClientInstaller(comp.PythonInstallComponent):
})
return places
def _get_pkgs(self):
pkgs = comp.PythonInstallComponent._get_pkgs(self)
for fn in REQ_PKGS:
full_name = sh.joinpths(settings.STACK_PKG_DIR, fn)
pkgs = utils.extract_pkg_list([full_name], self.distro, pkgs)
return pkgs
class NovaClientRuntime(comp.EmptyRuntime):
def __init__(self, *args, **kargs):

@ -18,6 +18,7 @@ from devstack import component as comp
from devstack import log as logging
from devstack import settings
from devstack import shell as sh
from devstack.components import nova
LOG = logging.getLogger("devstack.components.novnc")
@ -35,6 +36,9 @@ APP_OPTIONS = {
VNC_PROXY_APP: ['--flagfile-file', '%NOVA_CONF%', '--web'],
}
#the pkg json files novnc requires for installation
REQ_PKGS = ['n-vnc.json']
class NoVNCUninstaller(comp.PkgUninstallComponent):
def __init__(self, *args, **kargs):
@ -55,6 +59,13 @@ class NoVNCInstaller(comp.PkgInstallComponent):
})
return places
def _get_pkgs(self):
pkgs = comp.PkgInstallComponent._get_pkgs(self)
for fn in REQ_PKGS:
full_name = sh.joinpths(settings.STACK_PKG_DIR, fn)
pkgs = utils.extract_pkg_list([full_name], self.distro, pkgs)
return pkgs
class NoVNCRuntime(comp.ProgramRuntime):
def __init__(self, *args, **kargs):

@ -17,12 +17,17 @@
from devstack import component as comp
from devstack import log as logging
from devstack import settings
from devstack import shell as sh
from devstack import utils
LOG = logging.getLogger("devstack.components.openstackx")
#id
TYPE = settings.OPENSTACK_X
#the pkg json files novnc requires for installation
REQ_PKGS = ['general.json', 'openstackx.json']
class OpenstackXUninstaller(comp.PythonUninstallComponent):
def __init__(self, *args, **kargs):
@ -43,6 +48,13 @@ class OpenstackXInstaller(comp.PythonInstallComponent):
})
return places
def _get_pkgs(self):
pkgs = comp.PythonInstallComponent._get_pkgs(self)
for fn in REQ_PKGS:
full_name = sh.joinpths(settings.STACK_PKG_DIR, fn)
pkgs = utils.extract_pkg_list([full_name], self.distro, pkgs)
return pkgs
class OpenstackXRuntime(comp.EmptyRuntime):
def __init__(self, *args, **kargs):

@ -22,6 +22,7 @@ from devstack import log as logging
from devstack import settings
from devstack import shell as sh
from devstack import utils
from devstack.components import db
LOG = logging.getLogger("devstack.components.quantum")
@ -67,6 +68,9 @@ APP_OPTIONS = {
APP_Q_AGENT: ["%OVS_CONFIG_FILE%", "-v"],
}
#the pkg json files quantum requires for installation
REQ_PKGS = ['general.json', 'quantum.json']
class QuantumUninstaller(comp.PkgUninstallComponent):
def __init__(self, *args, **kargs):
@ -101,8 +105,11 @@ class QuantumInstaller(comp.PkgInstallComponent):
})
return places
def _get_pkglist(self):
pkglist = comp.PkgInstallComponent._get_pkglist(self)
def _get_pkgs(self):
pkglist = comp.PkgInstallComponent._get_pkgs(self)
for fn in REQ_PKGS:
full_name = sh.joinpths(settings.STACK_PKG_DIR, fn)
pkglist = utils.extract_pkg_list([full_name], self.distro, pkglist)
if self.q_vswitch_service:
listing_fn = sh.joinpths(settings.STACK_PKG_DIR, PKG_VSWITCH)
pkglist = utils.extract_pkg_list([listing_fn], self.distro, pkglist)

@ -17,12 +17,17 @@
from devstack import component as comp
from devstack import log as logging
from devstack import settings
from devstack import shell as sh
from devstack import utils
LOG = logging.getLogger("devstack.components.quantum_client")
#id
TYPE = settings.QUANTUM_CLIENT
#the pkg json files quantum client requires for installation
REQ_PKGS = ['general.json', 'quantum-client.json']
class QuantumClientUninstaller(comp.PythonUninstallComponent):
def __init__(self, *args, **kargs):
@ -43,6 +48,13 @@ class QuantumClientInstaller(comp.PythonInstallComponent):
})
return places
def _get_pkgs(self):
pkgs = comp.PythonInstallComponent._get_pkgs(self)
for fn in REQ_PKGS:
full_name = sh.joinpths(settings.STACK_PKG_DIR, fn)
pkgs = utils.extract_pkg_list([full_name], self.distro, pkgs)
return pkgs
class QuantumClientRuntime(comp.EmptyRuntime):
def __init__(self, *args, **kargs):

@ -22,6 +22,7 @@ from devstack import log as logging
from devstack import settings
from devstack import shell as sh
from devstack import trace as tr
from devstack import utils
LOG = logging.getLogger("devstack.components.rabbit")
@ -35,6 +36,9 @@ STATUS_CMD = ['service', "rabbitmq-server", "status"]
RESTART_CMD = ['service', "rabbitmq-server", "restart"]
PWD_CMD = ['rabbitmqctl', 'change_password', 'guest']
#the pkg json files rabbit mq server requires for installation
REQ_PKGS = ['rabbitmq.json']
class RabbitUninstaller(comp.PkgUninstallComponent):
def __init__(self, *args, **kargs):
@ -57,6 +61,13 @@ class RabbitInstaller(comp.PkgInstallComponent):
self.runtime.restart()
return parent_result
def _get_pkgs(self):
pkgs = comp.PkgInstallComponent._get_pkgs(self)
for fn in REQ_PKGS:
full_name = sh.joinpths(settings.STACK_PKG_DIR, fn)
pkgs = utils.extract_pkg_list([full_name], self.distro, pkgs)
return pkgs
class RabbitRuntime(comp.EmptyRuntime):
def __init__(self, *args, **kargs):

@ -26,9 +26,9 @@ LOG = logging.getLogger("devstack.components.swift")
TYPE = settings.SWIFT
class SwiftUninstaller(object):
class SwiftUninstaller(comp.ComponentBase):
def __init__(self, *args, **kargs):
pass
comp.ComponentBase.__init__(self, TYPE, *args, **kargs)
def unconfigure(self):
raise NotImplementedError()
@ -37,9 +37,9 @@ class SwiftUninstaller(object):
raise NotImplementedError()
class SwiftInstaller(object):
class SwiftInstaller(comp.ComponentBase):
def __init__(self, *args, **kargs):
pass
comp.ComponentBase.__init__(self, TYPE, *args, **kargs)
def download(self):
raise NotImplementedError()

@ -27,6 +27,10 @@ class BadRegexException(StackException):
pass
class DependencyException(StackException):
pass
class BadParamException(StackException):
pass

@ -26,23 +26,11 @@ from devstack import settings
from devstack import shell as sh
from devstack import utils
from devstack.components import db
from devstack.components import glance
from devstack.components import horizon
from devstack.components import keystone
from devstack.components import keystone_client
from devstack.components import nova
from devstack.components import nova_client
from devstack.components import novnc
from devstack.components import openstack_x
from devstack.components import quantum
from devstack.components import quantum_client
from devstack.components import rabbit
from devstack.components import swift
from devstack.packaging import apt
from devstack.packaging import yum
from devstack.progs import common
LOG = logging.getLogger("devstack.progs.actions")
#this map controls which distro has
@ -64,71 +52,6 @@ _WELCOME_MAP = {
# For actions in this list we will reverse the component order
_REVERSE_ACTIONS = [settings.UNINSTALL, settings.STOP]
# This determines what classes to use to install/uninstall/...
_ACTION_CLASSES = {
settings.INSTALL: {
settings.NOVA: nova.NovaInstaller,
settings.GLANCE: glance.GlanceInstaller,
settings.QUANTUM: quantum.QuantumInstaller,
settings.SWIFT: swift.SwiftInstaller,
settings.HORIZON: horizon.HorizonInstaller,
settings.KEYSTONE: keystone.KeystoneInstaller,
settings.DB: db.DBInstaller,
settings.RABBIT: rabbit.RabbitInstaller,
settings.KEYSTONE_CLIENT: keystone_client.KeyStoneClientInstaller,
settings.NOVA_CLIENT: nova_client.NovaClientInstaller,
settings.OPENSTACK_X: openstack_x.OpenstackXInstaller,
settings.NOVNC: novnc.NoVNCInstaller,
settings.QUANTUM_CLIENT: quantum_client.QuantumClientInstaller,
},
settings.UNINSTALL: {
settings.NOVA: nova.NovaUninstaller,
settings.GLANCE: glance.GlanceUninstaller,
settings.QUANTUM: quantum.QuantumUninstaller,
settings.SWIFT: swift.SwiftUninstaller,
settings.HORIZON: horizon.HorizonUninstaller,
settings.KEYSTONE: keystone.KeystoneUninstaller,
settings.DB: db.DBUninstaller,
settings.RABBIT: rabbit.RabbitUninstaller,
settings.KEYSTONE_CLIENT: keystone_client.KeyStoneClientUninstaller,
settings.NOVA_CLIENT: nova_client.NovaClientUninstaller,
settings.OPENSTACK_X: openstack_x.OpenstackXUninstaller,
settings.NOVNC: novnc.NoVNCUninstaller,
settings.QUANTUM_CLIENT: quantum_client.QuantumClientUninstaller,
},
settings.START: {
settings.NOVA: nova.NovaRuntime,
settings.GLANCE: glance.GlanceRuntime,
settings.QUANTUM: quantum.QuantumRuntime,
settings.SWIFT: swift.SwiftRuntime,
settings.HORIZON: horizon.HorizonRuntime,
settings.KEYSTONE: keystone.KeystoneRuntime,
settings.DB: db.DBRuntime,
settings.RABBIT: rabbit.RabbitRuntime,
settings.KEYSTONE_CLIENT: keystone_client.KeyStoneClientRuntime,
settings.NOVA_CLIENT: nova_client.NovaClientRuntime,
settings.OPENSTACK_X: openstack_x.OpenstackXRuntime,
settings.NOVNC: novnc.NoVNCRuntime,
settings.QUANTUM_CLIENT: quantum_client.QuantumClientRuntime,
},
settings.STOP: {
settings.NOVA: nova.NovaRuntime,
settings.GLANCE: glance.GlanceRuntime,
settings.QUANTUM: quantum.QuantumRuntime,
settings.SWIFT: swift.SwiftRuntime,
settings.HORIZON: horizon.HorizonRuntime,
settings.KEYSTONE: keystone.KeystoneRuntime,
settings.DB: db.DBRuntime,
settings.RABBIT: rabbit.RabbitRuntime,
settings.KEYSTONE_CLIENT: keystone_client.KeyStoneClientRuntime,
settings.NOVA_CLIENT: nova_client.NovaClientRuntime,
settings.OPENSTACK_X: openstack_x.OpenstackXRuntime,
settings.NOVNC: novnc.NoVNCRuntime,
settings.QUANTUM_CLIENT: quantum_client.QuantumClientRuntime,
},
}
def _clean_action(action):
if action is None:
return None
@ -143,14 +66,8 @@ def _get_pkg_manager(distro):
return cls(distro)
def _get_action_cls(action_name, component_name):
action_cls_map = _ACTION_CLASSES.get(action_name)
if not action_cls_map:
return None
return action_cls_map.get(component_name)
def _check_roots(action, rootdir, components):
#TODO the check is really pretty basic so should not be depended on...
to_skip = list()
if action == settings.INSTALL:
if sh.isdir(rootdir):
@ -276,25 +193,17 @@ def _uninstall(component_name, instance, skip_notrace):
raise
def _get_config():
cfg_fn = sh.canon_path(settings.STACK_CONFIG_LOCATION)
LOG.info("Loading config from [%s]" % (cfg_fn))
config_instance = cfg.EnvConfigParser()
config_instance.read(cfg_fn)
return config_instance
def _run_components(action_name, component_order, components, distro, root_dir, program_args):
LOG.info("Will %s [%s] (in that order) using root directory \"%s\"" % (action_name, ", ".join(component_order), root_dir))
non_components = set(components.keys()).difference(set(component_order))
if non_components:
LOG.info("Using reference components [%s]" % (", ".join(sorted(non_components))))
pkg_manager = _get_pkg_manager(distro)
config = _get_config()
config = common.get_config()
#form the active instances (this includes ones we won't use)
all_instances = dict()
for component in components.keys():
action_cls = _get_action_cls(action_name, component)
action_cls = common.get_action_cls(action_name, component)
instance = action_cls(instances=all_instances,
distro=distro,
packager=pkg_manager,
@ -334,40 +243,12 @@ def _run_components(action_name, component_order, components, distro, root_dir,
return results
def _get_def_components():
#this seems to be the default list of what to install by default
#ENABLED_SERVICES=${ENABLED_SERVICES:-g-api,g-reg,key,n-api,
#n-crt,n-obj,n-cpu,n-net,n-sch,n-novnc,n-xvnc,n-cauth,horizon,mysql,rabbit}
def_components = dict()
def_components[settings.GLANCE] = [
glance.GAPI,
glance.GREG,
]
def_components[settings.KEYSTONE] = []
#TODO add in xvnc?
def_components[settings.NOVA] = [
nova.NAPI,
nova.NCAUTH,
nova.NCERT,
nova.NCPU,
nova.NNET,
nova.NOBJ,
nova.NSCHED,
nova.NVOL,
]
def_components[settings.NOVNC] = []
def_components[settings.HORIZON] = []
def_components[settings.DB] = []
def_components[settings.RABBIT] = []
return def_components
def _run_action(args):
defaulted_components = False
components = settings.parse_components(args.pop("components"))
components = utils.parse_components(args.pop("components"))
if not components:
defaulted_components = True
components = _get_def_components()
components = common.get_default_components()
action = _clean_action(args.pop("action"))
if not action:
cprint("No valid action specified!", "red")
@ -391,29 +272,28 @@ def _run_action(args):
LOG.info("Activating default components [%s]" % (", ".join(sorted(components.keys()))))
#need to figure out dependencies for components (if any)
ignore_deps = args.pop('ignore_deps', False)
component_order = None
if not ignore_deps:
new_components = settings.resolve_dependencies(components.keys())
component_diff = new_components.difference(components.keys())
all_components_deps = common.get_components_deps(action, components)
component_diff = set(all_components_deps.keys()).difference(components.keys())
if component_diff:
LOG.info("Having to activate dependent components: [%s]" % (", ".join(sorted(component_diff))))
for new_component in component_diff:
components[new_component] = list()
component_order = utils.get_components_order(all_components_deps)
else:
component_order = components.keys()
#see if we have previously already done the components
#TODO the check is really pretty basic so should not be depended on...
component_skips = _check_roots(action, rootdir, components.keys())
component_skips = _check_roots(action, rootdir, component_order)
for c in component_skips:
components.pop(c)
if not components:
LOG.error("After checking the various components roots, no components ended up being specified!")
return False
#get the right component order (by priority)
component_order = settings.prioritize_components(components.keys())
component_order.remove(c)
#reverse them so that we stop in the reverse order
#and that we uninstall in the reverse order which seems to make sense
if action in _REVERSE_ACTIONS:
#reverse them so that we stop in the reverse order
#and that we uninstall in the reverse order which seems to make sense
component_order.reverse()
#add in any that will just be referenced but which will not actually do anything (ie the action will not be applied to these)
ref_components = settings.parse_components(args.pop("ref_components"))
ref_components = utils.parse_components(args.pop("ref_components"))
for c in ref_components.keys():
if c not in components:
components[c] = ref_components.get(c)

159
devstack/progs/common.py Normal file

@ -0,0 +1,159 @@
# 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 tempfile
from devstack import cfg
from devstack import settings
from devstack import shell as sh
from devstack.components import db
from devstack.components import glance
from devstack.components import horizon
from devstack.components import keystone
from devstack.components import keystone_client
from devstack.components import nova
from devstack.components import nova_client
from devstack.components import novnc
from devstack.components import openstack_x
from devstack.components import quantum
from devstack.components import quantum_client
from devstack.components import rabbit
from devstack.components import swift
# This determines what classes to use to install/uninstall/...
ACTION_CLASSES = {
settings.INSTALL: {
settings.NOVA: nova.NovaInstaller,
settings.GLANCE: glance.GlanceInstaller,
settings.QUANTUM: quantum.QuantumInstaller,
settings.SWIFT: swift.SwiftInstaller,
settings.HORIZON: horizon.HorizonInstaller,
settings.KEYSTONE: keystone.KeystoneInstaller,
settings.DB: db.DBInstaller,
settings.RABBIT: rabbit.RabbitInstaller,
settings.KEYSTONE_CLIENT: keystone_client.KeyStoneClientInstaller,
settings.NOVA_CLIENT: nova_client.NovaClientInstaller,
settings.OPENSTACK_X: openstack_x.OpenstackXInstaller,
settings.NOVNC: novnc.NoVNCInstaller,
settings.QUANTUM_CLIENT: quantum_client.QuantumClientInstaller,
},
settings.UNINSTALL: {
settings.NOVA: nova.NovaUninstaller,
settings.GLANCE: glance.GlanceUninstaller,
settings.QUANTUM: quantum.QuantumUninstaller,
settings.SWIFT: swift.SwiftUninstaller,
settings.HORIZON: horizon.HorizonUninstaller,
settings.KEYSTONE: keystone.KeystoneUninstaller,
settings.DB: db.DBUninstaller,
settings.RABBIT: rabbit.RabbitUninstaller,
settings.KEYSTONE_CLIENT: keystone_client.KeyStoneClientUninstaller,
settings.NOVA_CLIENT: nova_client.NovaClientUninstaller,
settings.OPENSTACK_X: openstack_x.OpenstackXUninstaller,
settings.NOVNC: novnc.NoVNCUninstaller,
settings.QUANTUM_CLIENT: quantum_client.QuantumClientUninstaller,
},
settings.START: {
settings.NOVA: nova.NovaRuntime,
settings.GLANCE: glance.GlanceRuntime,
settings.QUANTUM: quantum.QuantumRuntime,
settings.SWIFT: swift.SwiftRuntime,
settings.HORIZON: horizon.HorizonRuntime,
settings.KEYSTONE: keystone.KeystoneRuntime,
settings.DB: db.DBRuntime,
settings.RABBIT: rabbit.RabbitRuntime,
settings.KEYSTONE_CLIENT: keystone_client.KeyStoneClientRuntime,
settings.NOVA_CLIENT: nova_client.NovaClientRuntime,
settings.OPENSTACK_X: openstack_x.OpenstackXRuntime,
settings.NOVNC: novnc.NoVNCRuntime,
settings.QUANTUM_CLIENT: quantum_client.QuantumClientRuntime,
},
settings.STOP: {
settings.NOVA: nova.NovaRuntime,
settings.GLANCE: glance.GlanceRuntime,
settings.QUANTUM: quantum.QuantumRuntime,
settings.SWIFT: swift.SwiftRuntime,
settings.HORIZON: horizon.HorizonRuntime,
settings.KEYSTONE: keystone.KeystoneRuntime,
settings.DB: db.DBRuntime,
settings.RABBIT: rabbit.RabbitRuntime,
settings.KEYSTONE_CLIENT: keystone_client.KeyStoneClientRuntime,
settings.NOVA_CLIENT: nova_client.NovaClientRuntime,
settings.OPENSTACK_X: openstack_x.OpenstackXRuntime,
settings.NOVNC: novnc.NoVNCRuntime,
settings.QUANTUM_CLIENT: quantum_client.QuantumClientRuntime,
},
}
_FAKE_ROOT_DIR = tempfile.gettempdir()
def get_default_components():
#this seems to be the default list of what to install by default
#ENABLED_SERVICES=${ENABLED_SERVICES:-g-api,g-reg,key,n-api,
#n-crt,n-obj,n-cpu,n-net,n-sch,n-novnc,n-xvnc,n-cauth,horizon,mysql,rabbit}
def_components = dict()
def_components[settings.GLANCE] = [
glance.GAPI,
glance.GREG,
]
def_components[settings.KEYSTONE] = []
#TODO add in xvnc?
def_components[settings.NOVA] = [
nova.NAPI,
nova.NCAUTH,
nova.NCERT,
nova.NCPU,
nova.NNET,
nova.NOBJ,
nova.NSCHED,
nova.NVOL,
]
def_components[settings.NOVNC] = []
def_components[settings.HORIZON] = []
def_components[settings.DB] = []
def_components[settings.RABBIT] = []
return def_components
def get_action_cls(action_name, component_name):
action_cls_map = ACTION_CLASSES.get(action_name)
return action_cls_map.get(component_name)
def get_config():
cfg_fn = sh.canon_path(settings.STACK_CONFIG_LOCATION)
config_instance = cfg.EnvConfigParser()
config_instance.read(cfg_fn)
return config_instance
def get_components_deps(action_name, base_components):
all_components = dict()
active_names = list(base_components)
while len(active_names):
component = active_names.pop()
component_opts = base_components.get(component) or list()
cls = get_action_cls(action_name, component)
instance = cls(root=_FAKE_ROOT_DIR, opts=component_opts,
config=get_config())
deps = instance.get_dependencies()
if deps is None:
deps = set()
all_components[component] = set(deps)
for d in deps:
if d not in all_components and d not in active_names:
active_names.append(d)
return all_components

68
devstack/progs/deps.py Normal file

@ -0,0 +1,68 @@
# 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.
#requires http://pypi.python.org/pypi/termcolor
#but the colors make it worth it :-)
from termcolor import colored
from devstack import settings
from devstack import utils
from devstack.progs import common
PROG_NAME = "Dep. Lister"
DEF_ACTION = settings.INSTALL
def _print_deps(component, deps):
print(" + " + colored(component, "blue", attrs=['bold']))
if deps:
for d in sorted(deps):
print(" |")
print(" ------> %s" % (d))
def _clean_action(action):
if not action:
return DEF_ACTION
action = action.strip.lower()
if action not in settings.ACTIONS:
return DEF_ACTION
return action
def _run_dep_comps(args, rep, maxlen):
components = utils.parse_components(args.get("components"))
if not components:
components = dict()
for c in settings.COMPONENT_NAMES:
components[c] = list()
header = utils.center_text("Dependencies (defaulted)", rep, maxlen)
else:
header = utils.center_text("Dependencies", rep, maxlen)
print(header)
action = _clean_action(args.pop("action"))
msg = "For action %s" % (action)
print(utils.center_text(msg, rep, maxlen))
all_deps = common.get_components_deps(action, components)
for c in sorted(all_deps.keys()):
_print_deps(c, all_deps.get(c))
def run(args):
(rep, maxlen) = utils.welcome(PROG_NAME)
_run_dep_comps(args, rep, maxlen)
return True

@ -37,7 +37,7 @@ from devstack.components import quantum_client
from devstack.components import rabbit
from devstack.components import swift
PROG_NAME = "MISC"
PROG_NAME = "Describer"
#this determines how descriptions for components are found
_DESCR_MAP = {
@ -57,46 +57,24 @@ _DESCR_MAP = {
}
def log_deps(components):
shown = set()
left_show = list(components)
while left_show:
c = left_show.pop()
deps = settings.get_dependencies(c)
dep_str = "depends on:"
print(colored(c, "green", attrs=['bold']) + " depends on " + dep_str)
for d in deps:
print(" " + colored(d, "blue", attrs=['bold']))
shown.add(c)
for d in deps:
if d not in shown and d not in left_show:
left_show.append(d)
def _run_list_deps(args):
components = settings.parse_components(args.get("components"), True).keys()
components = sorted(components)
components.reverse()
return log_deps(components)
def _run_describe_comps(args):
components = settings.parse_components(args.get("components"), True)
def _run_describe_comps(args, rep, maxlen):
components = utils.parse_components(args.get("components"))
if not components:
components = dict()
for c in settings.COMPONENT_NAMES:
components[c] = list()
header = utils.center_text("Descriptions (defaulted)", rep, maxlen)
else:
header = utils.center_text("Descriptions", rep, maxlen)
print(header)
c_keys = sorted(components.keys())
for c in c_keys:
print("Name: " + colored(c, "green", attrs=['bold']) + "")
print("Name: " + colored(c, "blue", attrs=['bold']))
describer = _DESCR_MAP.get(c)
print(describer(components.get(c)))
def run(args):
(rep, maxlen) = utils.welcome(PROG_NAME)
if args.get('list_deps'):
header = utils.center_text("Dependencies", rep, maxlen)
print(header)
_run_list_deps(args)
if args.get('describe_comp'):
header = utils.center_text("Descriptions", rep, maxlen)
print(header)
_run_describe_comps(args)
_run_describe_comps(args, rep, maxlen)
return True

@ -53,7 +53,6 @@ DB = "db"
RABBIT = "rabbit"
OPENSTACK_X = 'openstack-x'
NOVNC = 'novnc'
COMPONENT_NAMES = [
NOVA, NOVA_CLIENT,
GLANCE,
@ -67,23 +66,6 @@ COMPONENT_NAMES = [
NOVNC,
]
# Ordering of install (lower priority means earlier)
COMPONENT_NAMES_PRIORITY = {
DB: 1,
RABBIT: 2,
KEYSTONE: 3,
GLANCE: 4,
QUANTUM: 4,
SWIFT: 4,
NOVA_CLIENT: 4,
NOVA: 5,
KEYSTONE_CLIENT: 6,
OPENSTACK_X: 6,
NOVNC: 6,
HORIZON: 10,
QUANTUM_CLIENT: 11,
}
# When a component is asked for it may
# need another component, that dependency
# mapping is listed here
@ -110,9 +92,6 @@ COMPONENT_TRACE_DIR = "traces"
COMPONENT_APP_DIR = "app"
COMPONENT_CONFIG_DIR = "config"
# This regex is used to extract a components options (if any) and its name
EXT_COMPONENT = re.compile(r"^\s*([\w-]+)(?:\((.*)\))?\s*$")
# Program
# actions
INSTALL = "install"
@ -135,162 +114,4 @@ KNOWN_DISTROS = {
}
# The pip files that each component needs
PIP_MAP = {
NOVA:
[],
GLANCE:
[],
KEYSTONE:
[
os.path.join(STACK_PIP_DIR, 'keystone.json'),
],
HORIZON:
[
os.path.join(STACK_PIP_DIR, 'horizon.json'),
],
SWIFT:
[],
KEYSTONE_CLIENT:
[],
DB:
[],
RABBIT:
[],
QUANTUM:
[],
QUANTUM_CLIENT:
[],
}
# The pkg files that each component needs
PKG_MAP = {
NOVA:
[
os.path.join(STACK_PKG_DIR, "general.json"),
os.path.join(STACK_PKG_DIR, "nova.json"),
#nova may add others in if it finds that u are asking for a additional components
],
NOVA_CLIENT:
[
os.path.join(STACK_PKG_DIR, "general.json"),
os.path.join(STACK_PKG_DIR, "nova-client.json"),
],
GLANCE:
[
os.path.join(STACK_PKG_DIR, "general.json"),
os.path.join(STACK_PKG_DIR, 'glance.json'),
],
KEYSTONE:
[
os.path.join(STACK_PKG_DIR, "general.json"),
os.path.join(STACK_PKG_DIR, 'keystone.json'),
],
HORIZON:
[
os.path.join(STACK_PKG_DIR, "general.json"),
os.path.join(STACK_PKG_DIR, 'horizon.json'),
],
SWIFT:
[
os.path.join(STACK_PKG_DIR, "general.json"),
os.path.join(STACK_PKG_DIR, 'swift.json'),
],
KEYSTONE_CLIENT:
[
os.path.join(STACK_PKG_DIR, "general.json"),
os.path.join(STACK_PKG_DIR, "keystone-client.json"),
],
QUANTUM:
[
os.path.join(STACK_PKG_DIR, "general.json"),
os.path.join(STACK_PKG_DIR, "quantum.json"),
#quantum may add others in if it finds that u are asking for a openvswitch
],
QUANTUM_CLIENT:
[
os.path.join(STACK_PKG_DIR, "general.json"),
os.path.join(STACK_PKG_DIR, "quantum-client.json"),
],
DB:
[
os.path.join(STACK_PKG_DIR, 'db.json'),
],
RABBIT:
[
os.path.join(STACK_PKG_DIR, 'rabbitmq.json'),
],
OPENSTACK_X:
[
os.path.join(STACK_PKG_DIR, "general.json"),
os.path.join(STACK_PKG_DIR, 'openstackx.json'),
],
NOVNC:
[
os.path.join(STACK_PKG_DIR, 'n-vnc.json'),
],
}
def get_dependencies(component):
return sorted(COMPONENT_DEPENDENCIES.get(component, list()))
def resolve_dependencies(components):
active_components = list(components)
new_components = set()
while active_components:
curr_comp = active_components.pop()
component_deps = get_dependencies(curr_comp)
new_components.add(curr_comp)
for c in component_deps:
if c in new_components or c in active_components:
pass
else:
active_components.append(c)
return new_components
def prioritize_components(components):
#get the right component order (by priority)
mporder = dict()
for c in components:
priority = COMPONENT_NAMES_PRIORITY.get(c)
if priority is None:
priority = sys.maxint
mporder[c] = priority
#sort by priority value
priority_order = sorted(mporder.iteritems(), key=operator.itemgetter(1))
#extract the final list ordering
component_order = [x[0] for x in priority_order]
return component_order
def parse_components(components, assume_all=False):
#none provided, init it
if not components:
components = list()
adjusted_components = dict()
for c in components:
mtch = EXT_COMPONENT.match(c)
if mtch:
component_name = mtch.group(1).lower().strip()
if component_name in COMPONENT_NAMES:
component_opts = mtch.group(2)
components_opts_cleaned = list()
if not component_opts:
pass
else:
sp_component_opts = component_opts.split(",")
for co in sp_component_opts:
cleaned_opt = co.strip()
if cleaned_opt:
components_opts_cleaned.append(cleaned_opt)
adjusted_components[component_name] = components_opts_cleaned
#should we adjust them to be all the components?
if not adjusted_components and assume_all:
all_components = dict()
for c in COMPONENT_NAMES:
all_components[c] = list()
adjusted_components = all_components
return adjusted_components

@ -37,6 +37,7 @@ from devstack import version
PARAM_SUB_REGEX = re.compile(r"%([\w\d]+?)%")
EXT_COMPONENT = re.compile(r"^\s*([\w-]+)(?:\((.*)\))?\s*$")
LOG = logging.getLogger("devstack.util")
TEMPLATE_EXT = ".tpl"
@ -157,27 +158,85 @@ def determine_distro():
return (found_os, plt)
def extract_pip_list(fns, distro):
all_pkgs = dict()
def extract_pip_list(fns, distro, all_pips=None):
if not all_pips:
all_pips = dict()
for fn in fns:
js = load_json(fn)
distro_pkgs = js.get(distro)
if distro_pkgs:
combined = dict(all_pkgs)
for (pkgname, pkginfo) in distro_pkgs.items():
#we currently just overwrite
combined[pkgname] = pkginfo
all_pkgs = combined
return all_pkgs
distro_pips = js.get(distro)
if distro_pips:
all_pips.update(distro_pips)
return all_pips
def get_pip_list(distro, component):
LOG.info("Getting pip packages for distro %s and component %s." % (distro, component))
fns = settings.PIP_MAP.get(component)
if fns is None:
return dict()
else:
return extract_pip_list(fns, distro)
def get_components_order(components):
#deep copy so components isn't messed with
all_components = dict()
for (name, deps) in components.items():
all_components[name] = set(deps)
#figure out which ones have no one depending on them
no_deps_components = set()
for (name, deps) in all_components.items():
referenced = False
for (_name, _deps) in all_components.items():
if _name == name:
continue
else:
if name in _deps:
referenced = True
break
if not referenced:
no_deps_components.add(name)
if not no_deps_components:
msg = "Components specifed have no root components, there is most likely a dependency cycle!"
raise excp.DependencyException(msg)
#now we have to do a quick check to ensure no component is causing a cycle
for (root, deps) in all_components.items():
#DFS down through the "roots" deps and there deps and so on and
#ensure that nobody is referencing the "root" component name,
#that would mean there is a cycle if a dependency of the "root" is.
active_deps = list(deps)
checked_deps = dict()
while len(active_deps):
dep = active_deps.pop()
itsdeps = all_components.get(dep)
checked_deps[dep] = True
if root in itsdeps:
msg = "Circular dependency between component %s and component %s!" % (root, dep)
raise excp.DependencyException(msg)
else:
for d in itsdeps:
if d not in checked_deps and d not in active_deps:
active_deps.append(d)
#now form the order
#basically a topological sorting
#https://en.wikipedia.org/wiki/Topological_sorting
ordering = list()
no_edges = set(no_deps_components)
while len(no_edges):
node = no_edges.pop()
ordering.append(node)
its_deps = all_components.get(node)
while len(its_deps):
name = its_deps.pop()
referenced = False
for (_name, _deps) in all_components.items():
if _name == name:
continue
else:
if name in _deps:
referenced = True
break
if not referenced:
no_edges.add(name)
#should now be no edges else something bad happended
for (_, deps) in all_components.items():
if len(deps):
msg = "Your specified components have at least one cycle!"
raise excp.DependencyException(msg)
#reverse so its in the right order for us
ordering.reverse()
return ordering
def extract_pkg_list(fns, distro, all_pkgs=None):
@ -187,35 +246,10 @@ def extract_pkg_list(fns, distro, all_pkgs=None):
js = load_json(fn)
distro_pkgs = js.get(distro)
if distro_pkgs:
combined = dict(all_pkgs)
for (pkgname, pkginfo) in distro_pkgs.items():
if pkgname in all_pkgs.keys():
oldpkginfo = all_pkgs.get(pkgname) or dict()
newpkginfo = dict(oldpkginfo)
for (infokey, infovalue) in pkginfo.items():
#this is expected to be a list of cmd actions
#so merge that accordingly
if(infokey == settings.PRE_INSTALL or
infokey == settings.POST_INSTALL):
oldinstalllist = oldpkginfo.get(infokey) or []
infovalue = oldinstalllist + infovalue
newpkginfo[infokey] = infovalue
combined[pkgname] = newpkginfo
else:
combined[pkgname] = pkginfo
all_pkgs = combined
all_pkgs.update(distro_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):
return os.linesep.join(pieces)
@ -341,6 +375,30 @@ def goodbye(worked):
print(msg)
def parse_components(components):
#none provided, init it
if not components:
components = list()
adjusted_components = dict()
for c in components:
mtch = EXT_COMPONENT.match(c)
if mtch:
component_name = mtch.group(1).lower().strip()
if component_name in settings.COMPONENT_NAMES:
component_opts = mtch.group(2)
components_opts_cleaned = list()
if not component_opts:
pass
else:
sp_component_opts = component_opts.split(",")
for co in sp_component_opts:
cleaned_opt = co.strip()
if cleaned_opt:
components_opts_cleaned.append(cleaned_opt)
adjusted_components[component_name] = components_opts_cleaned
return adjusted_components
def welcome(ident):
ver_str = version.version_string()
lower = "|"

10
stack

@ -39,16 +39,18 @@ from devstack import utils
#these are the program runtimes that actually do the running
from devstack.progs import actions
from devstack.progs import misc
from devstack.progs import describe
from devstack.progs import deps
def main():
#parse and get it done!
args = opts.parse()
#figure out what to do
module = None
if args.get('list_deps') or args.get('describe_comp'):
module = misc
if args.get('describe_comp'):
module = describe
elif args.get('list_deps'):
module = deps
else:
module = actions
try: