diff --git a/conf/horizon/horizon_settings.py b/conf/horizon/horizon_settings.py index 05ddfe7b..1350d8e2 100644 --- a/conf/horizon/horizon_settings.py +++ b/conf/horizon/horizon_settings.py @@ -40,7 +40,7 @@ HORIZON_CONFIG = { 'user_home': 'dashboard.views.user_home', } -OPENSTACK_HOST = "127.0.0.1" +OPENSTACK_HOST = "%OPENSTACK_HOST%" OPENSTACK_KEYSTONE_URL = "http://%s:5000/v2.0" % OPENSTACK_HOST # FIXME: this is only needed until keystone fixes its GET /tenants call # so that it doesn't return everything for admins diff --git a/conf/pips/horizon.json b/conf/pips/horizon.json index f6d4d940..edc2a81c 100644 --- a/conf/pips/horizon.json +++ b/conf/pips/horizon.json @@ -8,7 +8,9 @@ "skip_uninstall_errors": true }, "pycrypto": { - "version": "2.3" + "version": "2.3", + #this seems broke on uninstall so skip errors on uinstall + "skip_uninstall_errors": true } }, "rhel-6": {} diff --git a/conf/pkgs/nova-client.json b/conf/pkgs/nova-client.json new file mode 100644 index 00000000..e8933d4e --- /dev/null +++ b/conf/pkgs/nova-client.json @@ -0,0 +1,17 @@ +# This is a extended json package definition file +# We allow simple comments (lines starting with a hash symbol) +{ + "ubuntu-oneiric": { + "python-prettytable": { + "version": "0.5-1ubuntu1", + "allowed": ">=", + "removable": true + }, + "python-argparse": { + "version": "1.1-1ubuntu1", + "allowed": ">=", + "removable": false + } + }, + "rhel-6": {} +} diff --git a/conf/pkgs/openstackx.json b/conf/pkgs/openstackx.json new file mode 100644 index 00000000..c10e6cf6 --- /dev/null +++ b/conf/pkgs/openstackx.json @@ -0,0 +1,6 @@ +# This is a extended json package definition file +# We allow simple comments (lines starting with a hash symbol) +{ + "ubuntu-oneiric": {}, + "rhel-6": {} +} diff --git a/devstack/Component.py b/devstack/Component.py index 8da4140f..c37d44d2 100644 --- a/devstack/Component.py +++ b/devstack/Component.py @@ -130,7 +130,7 @@ class PkgInstallComponent(ComponentBase, InstallComponent): am_downloaded += 1 return am_downloaded - def _get_param_map(self, _=None): + def _get_param_map(self, config_fn): return None def install(self): @@ -147,21 +147,21 @@ class PkgInstallComponent(ComponentBase, InstallComponent): def pre_install(self): pkgs = Util.get_pkg_list(self.distro, self.component_name) if(len(pkgs)): - mp = self._get_param_map() + mp = self._get_param_map(None) self.packager.pre_install(pkgs, mp) return self.tracedir def post_install(self): pkgs = Util.get_pkg_list(self.distro, self.component_name) if(len(pkgs)): - mp = self._get_param_map() + mp = self._get_param_map(None) self.packager.post_install(pkgs, mp) return self.tracedir def _get_config_files(self): return list() - def _config_adjust(self, contents, _): + def _config_adjust(self, contents, config_fn): return contents def _get_full_config_name(self, name): @@ -360,10 +360,10 @@ class ProgramRuntime(ComponentBase, RuntimeComponent): def _get_apps_to_start(self): return list() - def _get_app_options(self, _): + def _get_app_options(self, app_name): return list() - def _get_param_map(self, _=None): + def _get_param_map(self, app_name): return { 'ROOT': self.appdir, } diff --git a/devstack/Db.py b/devstack/Db.py index 015aff48..16a29e50 100644 --- a/devstack/Db.py +++ b/devstack/Db.py @@ -67,7 +67,7 @@ class DBInstaller(PkgInstallComponent): PkgInstallComponent.__init__(self, TYPE, *args, **kargs) self.runtime = DBRuntime(*args, **kargs) - def _get_param_map(self, fn=None): + def _get_param_map(self, config_fn): #this dictionary will be used for parameter replacement #in pre-install and post-install sections out = dict() @@ -87,7 +87,7 @@ class DBInstaller(PkgInstallComponent): if(dbactions and dbactions.get('grant_all')): #update the DB to give user 'USER'@'%' full control of the all databases: grant_cmd = dbactions.get('grant_all') - params = self._get_param_map() + params = self._get_param_map(None) cmds = list() cmds.append({ 'cmd': grant_cmd, diff --git a/devstack/Glance.py b/devstack/Glance.py index 974c501c..49623229 100644 --- a/devstack/Glance.py +++ b/devstack/Glance.py @@ -155,7 +155,7 @@ class GlanceInstaller(PythonInstallComponent): #nothing modified so just return the original return contents - def _get_param_map(self, fn=None): + def _get_param_map(self, config_fn): #this dict will be used to fill in the configuration #params with actual values mp = dict() diff --git a/devstack/Horizon.py b/devstack/Horizon.py index d23d9f99..bda5e702 100644 --- a/devstack/Horizon.py +++ b/devstack/Horizon.py @@ -25,11 +25,15 @@ from Component import (PythonUninstallComponent, LOG = Logger.getLogger("install.horizon") TYPE = Util.HORIZON + ROOT_HORIZON = 'horizon' HORIZON_NAME = 'horizon' ROOT_DASH = 'openstack-dashboard' DASH_NAME = 'dashboard' +HORIZON_PY_CONF = "horizon_settings.py" +CONFIGS = [HORIZON_PY_CONF] + class HorizonUninstaller(PythonUninstallComponent): def __init__(self, *args, **kargs): @@ -64,6 +68,17 @@ class HorizonInstaller(PythonInstallComponent): }) return py_dirs + def _get_config_files(self): + #these are the config files we will be adjusting + return list(CONFIGS) + + def _get_param_map(self, config_fn): + #this dict will be used to fill in the configuration + #params with actual values + mp = dict() + mp['OPENSTACK_HOST'] = Util.get_host_ip(self.cfg) + return mp + class HorizonRuntime(NullRuntime): def __init__(self, *args, **kargs): diff --git a/devstack/Keystone.py b/devstack/Keystone.py index 12426e75..0014fe6a 100644 --- a/devstack/Keystone.py +++ b/devstack/Keystone.py @@ -85,7 +85,7 @@ class KeystoneInstaller(PythonInstallComponent): Db.create_db(self.cfg, DB_NAME) def _setup_data(self): - params = self._get_param_map() + params = self._get_param_map(None) cmds = _keystone_setup_cmds(self.all_components) execute_template(*cmds, params=params, ignore_missing=True) @@ -115,7 +115,7 @@ class KeystoneInstaller(PythonInstallComponent): #nothing modified so just return the original return contents - def _get_param_map(self, fn=None): + def _get_param_map(self, config_fn): #these be used to fill in the configuration/cmds + #params with actual values mp = dict() diff --git a/devstack/Nova.py b/devstack/Nova.py index 528aa72d..c99f5e5a 100644 --- a/devstack/Nova.py +++ b/devstack/Nova.py @@ -67,7 +67,7 @@ class NovaInstaller(InstallComponent): lines = nc.generate() return os.linesep.join(lines) - def _get_param_map(self, fn=None): + def _get_param_map(self, config_fn): # Not used. NovaConf will be used to generate the config file mp = dict() return mp diff --git a/devstack/NovaClient.py b/devstack/NovaClient.py new file mode 100644 index 00000000..f73121b2 --- /dev/null +++ b/devstack/NovaClient.py @@ -0,0 +1,59 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# +# 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 Logger +import Util + +#TODO fix these +from Component import (PythonUninstallComponent, + PythonInstallComponent, + NullRuntime) + + +LOG = Logger.getLogger("install.nova.client") +TYPE = Util.NOVA_CLIENT + + +class NovaClientUninstaller(PythonUninstallComponent): + def __init__(self, *args, **kargs): + PythonUninstallComponent.__init__(self, TYPE, *args, **kargs) + + +class NovaClientInstaller(PythonInstallComponent): + def __init__(self, *args, **kargs): + PythonInstallComponent.__init__(self, TYPE, *args, **kargs) + self.git_loc = self.cfg.get("git", "novaclient_repo") + self.git_branch = self.cfg.get("git", "novaclient_branch") + + def _get_download_locations(self): + places = PythonInstallComponent._get_download_locations(self) + places.append({ + 'uri': self.git_loc, + 'branch': self.git_branch, + }) + return places + + def _get_param_map(self, config_fn): + #this dict will be used to fill in the configuration + #params with actual values + mp = dict() + mp['DEST'] = self.appdir + mp['OPENSTACK_HOST'] = Util.get_host_ip(self.cfg) + return mp + +class NovaClientRuntime(NullRuntime): + def __init__(self, *args, **kargs): + NullRuntime.__init__(self, TYPE, *args, **kargs) diff --git a/devstack/OpenstackX.py b/devstack/OpenstackX.py new file mode 100644 index 00000000..c9caa155 --- /dev/null +++ b/devstack/OpenstackX.py @@ -0,0 +1,52 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# +# 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 Logger +import Util + +#TODO fix these +from Component import (PythonUninstallComponent, + PythonInstallComponent, + NullRuntime) + + +LOG = Logger.getLogger("install.openstackx") +TYPE = Util.OPENSTACK_X + + +class OpenstackXUninstaller(PythonUninstallComponent): + def __init__(self, *args, **kargs): + PythonUninstallComponent.__init__(self, TYPE, *args, **kargs) + + +class OpenstackXInstaller(PythonInstallComponent): + def __init__(self, *args, **kargs): + PythonInstallComponent.__init__(self, TYPE, *args, **kargs) + self.git_loc = self.cfg.get("git", "openstackx_repo") + self.git_branch = self.cfg.get("git", "openstackx_branch") + + def _get_download_locations(self): + places = PythonInstallComponent._get_download_locations(self) + places.append({ + 'uri': self.git_loc, + 'branch': self.git_branch, + }) + return places + + +class OpenstackXRuntime(NullRuntime): + def __init__(self, *args, **kargs): + NullRuntime.__init__(self, TYPE, *args, **kargs) diff --git a/devstack/Options.py b/devstack/Options.py index a3eb43e3..29896657 100644 --- a/devstack/Options.py +++ b/devstack/Options.py @@ -20,9 +20,11 @@ import Util def parse(): - versionstr = "%prog v" + Util.VERSION_STR - parser = OptionParser(version=versionstr) + #version + version_str = "%prog v" + Util.VERSION_STR + parser = OptionParser(version=version_str) + #non-boolean options known_actions = sorted(Util.ACTIONS) actions = "(" + ", ".join(known_actions) + ")" parser.add_option("-a", "--action", @@ -47,19 +49,35 @@ def parse(): dest="component", help="stack component, ie %s" % (components)) + #boolean options parser.add_option("-f", "--force", action="store_true", dest="force", help="force ACTION even if no trace found (ACTION dependent)", default=False) + parser.add_option("-i", "--ignoredeps", + action="store_true", + dest="ignore_deps", + help="ignore dependencies when performing ACTION", + default=False) + + parser.add_option("-l", "--listdeps", + action="store_true", + dest="list_deps", + help="just show dependencies of COMPONENT", + default=False) + (options, args) = parser.parse_args() #extract only what we care about output = dict() - output['component'] = options.component + output['components'] = options.component output['dir'] = options.dir output['action'] = options.action - output['extras'] = args + output['list_deps'] = options.list_deps output['force'] = options.force + output['ignore_deps'] = options.ignore_deps + output['extras'] = args + return output diff --git a/devstack/Util.py b/devstack/Util.py index 1317db89..b0572599 100644 --- a/devstack/Util.py +++ b/devstack/Util.py @@ -53,6 +53,7 @@ PARAM_SUB_REGEX = re.compile(r"%([\w\d]+?)%") #component name mappings NOVA = "nova" +NOVA_CLIENT = 'nova-client' GLANCE = "glance" QUANTUM = "quantum" SWIFT = "swift" @@ -61,21 +62,27 @@ KEYSTONE = "keystone" KEYSTONE_CLIENT = 'keystone-client' DB = "db" RABBIT = "rabbit" -COMPONENT_NAMES = [NOVA, GLANCE, QUANTUM, - SWIFT, HORIZON, KEYSTONE, - DB, RABBIT, KEYSTONE_CLIENT] +OPENSTACK_X = 'openstack-x' +COMPONENT_NAMES = [NOVA, NOVA_CLIENT, + GLANCE, QUANTUM, + SWIFT, HORIZON, + KEYSTONE, KEYSTONE_CLIENT, + OPENSTACK_X, + DB, RABBIT] #ordering of install (lower priority means earlier) NAMES_PRIORITY = { DB: 1, - RABBIT: 1, - KEYSTONE: 2, - GLANCE: 3, - QUANTUM: 3, - NOVA: 3, - SWIFT: 3, - HORIZON: 3, - KEYSTONE_CLIENT: 4, + RABBIT: 2, + KEYSTONE: 3, + GLANCE: 4, + QUANTUM: 4, + SWIFT: 4, + NOVA: 5, + KEYSTONE_CLIENT: 6, + NOVA_CLIENT: 6, + OPENSTACK_X: 6, + HORIZON: 10, } #when a component is asked for it may @@ -89,7 +96,7 @@ COMPONENT_DEPENDENCIES = { KEYSTONE: [DB], NOVA: [KEYSTONE, GLANCE, DB, RABBIT], SWIFT: [], - HORIZON: [KEYSTONE_CLIENT, GLANCE], + HORIZON: [KEYSTONE_CLIENT, GLANCE, NOVA_CLIENT, OPENSTACK_X], QUANTUM: [], } @@ -163,6 +170,11 @@ PKG_MAP = { Shell.joinpths(STACK_CONFIG_DIR, "pkgs", "nova.json"), Shell.joinpths(STACK_CONFIG_DIR, "pkgs", "general.json"), ], + NOVA_CLIENT: + [ + Shell.joinpths(STACK_CONFIG_DIR, "pkgs", "nova-client.json"), + Shell.joinpths(STACK_CONFIG_DIR, "pkgs", "general.json"), + ], GLANCE: [ Shell.joinpths(STACK_CONFIG_DIR, "pkgs", "general.json"), @@ -195,6 +207,10 @@ PKG_MAP = { [ Shell.joinpths(STACK_CONFIG_DIR, "pkgs", 'rabbitmq.json'), ], + OPENSTACK_X: + [ + Shell.joinpths(STACK_CONFIG_DIR, "pkgs", 'openstackx.json'), + ], } LOG = Logger.getLogger("install.util") diff --git a/stack b/stack index c32b3ad2..d418c144 100755 --- a/stack +++ b/stack @@ -26,9 +26,11 @@ import Options #TODO fix these from Util import (welcome, rcf8222date, determine_os, prioritize_components, resolve_dependencies) -from Util import (NOVA, GLANCE, QUANTUM, SWIFT, KEYSTONE, HORIZON, DB, RABBIT, KEYSTONE_CLIENT, - INSTALL, UNINSTALL, START, STOP, - ACTIONS, COMPONENT_NAMES, NAMES_PRIORITY, +from Util import (NOVA, GLANCE, QUANTUM, SWIFT, KEYSTONE, + HORIZON, DB, RABBIT, KEYSTONE_CLIENT, + NOVA_CLIENT, OPENSTACK_X) +from Util import(INSTALL, UNINSTALL, START, STOP, + ACTIONS, COMPONENT_NAMES, NAMES_PRIORITY, COMPONENT_DEPENDENCIES, UBUNTU11, RHEL6, STACK_CFG_LOC, DEVSTACK) from Shell import (mkdir, joinpths, unlink) @@ -45,6 +47,8 @@ import Db import Rabbit import Config import KeystoneClient +import NovaClient +import OpenstackX LOG = Logger.getLogger("install") @@ -60,6 +64,8 @@ ACTION_CLASSES = { DB: Db.DBInstaller, RABBIT: Rabbit.RabbitInstaller, KEYSTONE_CLIENT: KeystoneClient.KeyStoneClientInstaller, + NOVA_CLIENT: NovaClient.NovaClientInstaller, + OPENSTACK_X: OpenstackX.OpenstackXInstaller, }, UNINSTALL: { NOVA: Nova.NovaUninstaller, @@ -71,6 +77,8 @@ ACTION_CLASSES = { DB: Db.DBUninstaller, RABBIT: Rabbit.RabbitUninstaller, KEYSTONE_CLIENT: KeystoneClient.KeyStoneClientUninstaller, + NOVA_CLIENT: NovaClient.NovaClientUninstaller, + OPENSTACK_X: OpenstackX.OpenstackXUninstaller, }, START: { NOVA: Nova.NovaRuntime, @@ -82,6 +90,8 @@ ACTION_CLASSES = { DB: Db.DBRuntime, RABBIT: Rabbit.RabbitRuntime, KEYSTONE_CLIENT: KeystoneClient.KeyStoneClientRuntime, + NOVA_CLIENT: NovaClient.NovaClientRuntime, + OPENSTACK_X: OpenstackX.OpenstackXRuntime, }, STOP: { NOVA: Nova.NovaRuntime, @@ -93,6 +103,8 @@ ACTION_CLASSES = { DB: Db.DBRuntime, RABBIT: Rabbit.RabbitRuntime, KEYSTONE_CLIENT: KeystoneClient.KeyStoneClientRuntime, + NOVA_CLIENT: NovaClient.NovaClientRuntime, + OPENSTACK_X: OpenstackX.OpenstackXRuntime, }, } @@ -259,49 +271,80 @@ def check_python(): return True -def main(): - if(not check_python()): - LOG.error("Your python version is to old, please upgrade to >= 2.6!") - return 1 - me = __file__ - args = Options.parse() - components = args.pop("component") or [] +def run_list_only(prog, args): + components = args.pop("components") or [] + #clean the names up and see what is valid after an intersection + components = set([x.lower().strip() for x in components]) + components = set(COMPONENT_NAMES).intersection(components) if(len(components) == 0): - #assume user wants them all components = list(COMPONENT_NAMES) + components = sorted(components) + components.reverse() + shown = set() + left_show = list(components) + while(len(left_show) != 0): + c = left_show.pop() + deps = COMPONENT_DEPENDENCIES.get(c) or [] + cname = "" + if(len(deps) >= 1): + cname = "component" + if(cname > 1): + cname += "s" + cname += ":" + if(len(deps) == 0): + cname = "no components." + LOG.info("%s depends on %s" % (c, cname)) + if(len(deps)): + for d in deps: + LOG.info("\t%s" % (d)) + shown.add(c) + for d in deps: + if(d not in shown and d not in left_show): + left_show.append(d) + return True + + +def run_action(prog, args): + components = args.pop("components") or [] + if(len(components) == 0): + LOG.error("No components specified!") + LOG.info("Perhaps you should try %s --help" % (prog)) + return False else: #clean the names up and see what is valid after an intersection components = set([x.lower().strip() for x in components]) components = set(COMPONENT_NAMES).intersection(components) if(len(components) == 0): LOG.error("No valid components specified!") - LOG.info("Perhaps you should try %s --help" % (me)) - return 1 + LOG.info("Perhaps you should try %s --help" % (prog)) + return False + #extract + normalize the action + ignore_deps = args.pop('ignore_deps', False) action = args.pop("action") or "" - #normalize the action action = action.strip().lower() if(not (action in ACTIONS)): LOG.error("No valid action specified!") - LOG.info("Perhaps you should try %s --help" % (me)) - return 1 + LOG.info("Perhaps you should try %s --help" % (prog)) + return False rootdir = args.pop("dir") or "" if(len(rootdir) == 0 or not check_root(action, rootdir)): LOG.error("No valid root directory specified!") - LOG.info("Perhaps you should try %s --help" % (me)) - return 1 + LOG.info("Perhaps you should try %s --help" % (prog)) + return False #ensure os/distro is known (install_os, plt) = determine_os() if(install_os == None): LOG.error("Unsupported operating system/distro: %s" % (plt)) - return 1 + return False #start it welcome(action) #need to figure out dependencies for components (if any) - new_components = resolve_dependencies(action, components) - component_diff = new_components.difference(components) - if(len(component_diff)): - LOG.info("Having to install dependent components: [%s]" % (",".join(component_diff))) - components = new_components + if(not ignore_deps): + new_components = resolve_dependencies(action, components) + component_diff = new_components.difference(components) + if(len(component_diff)): + LOG.info("Having to install dependent components: [%s]" % (", ".join(component_diff))) + components = new_components #get the right component order (by priority) components = prioritize_components(components) #now do it! @@ -311,7 +354,27 @@ def main(): if(resultList and len(resultList)): msg = "Check [%s] for traces of what happened." % (", ".join(resultList)) LOG.info(msg) - return 0 + return True + + +def main(): + if(not check_python()): + LOG.error("Your python version is to old, please upgrade to >= 2.6!") + return 1 + #parse and get it done! + args = Options.parse() + me = os.path.basename(sys.argv[0]) + #figure out what to do + only_list_deps = args.pop('list_deps', False) + rc_ok = False + if(only_list_deps): + rc_ok = run_list_only(me, args) + else: + rc_ok = run_action(me, args) + if(rc_ok): + return 0 + else: + return 1 if __name__ == "__main__":