289 lines
9.4 KiB
Python
289 lines
9.4 KiB
Python
# 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 json
|
|
import netifaces
|
|
import operator
|
|
import os
|
|
import platform
|
|
import re
|
|
import string
|
|
import sys
|
|
|
|
from termcolor import colored
|
|
|
|
from devstack import constants
|
|
from devstack import exceptions as excp
|
|
from devstack import log as logging
|
|
from devstack import shell as sh
|
|
from devstack import version
|
|
|
|
PARAM_SUB_REGEX = re.compile(r"%([\w\d]+?)%")
|
|
LOG = logging.getLogger("devstack.util")
|
|
|
|
|
|
def get_dependencies(component):
|
|
deps = constants.COMPONENT_DEPENDENCIES.get(component, list())
|
|
return sorted(deps)
|
|
|
|
|
|
def resolve_dependencies(components):
|
|
new_components = list()
|
|
for c in components:
|
|
component_deps = list(set(fetch_dependencies(c)))
|
|
if(len(component_deps)):
|
|
new_components = new_components + component_deps
|
|
new_components.append(c)
|
|
return set(new_components)
|
|
|
|
|
|
def execute_template(*cmds, **kargs):
|
|
if(not cmds or len(cmds) == 0):
|
|
return
|
|
params_replacements = kargs.pop('params')
|
|
ignore_missing = kargs.pop('ignore_missing', False)
|
|
for cmdinfo in cmds:
|
|
cmd_to_run_templ = cmdinfo.get("cmd")
|
|
cmd_to_run = list()
|
|
for piece in cmd_to_run_templ:
|
|
if(params_replacements and len(params_replacements)):
|
|
cmd_to_run.append(param_replace(piece, params_replacements,
|
|
ignore_missing=ignore_missing))
|
|
else:
|
|
cmd_to_run.append(piece)
|
|
stdin_templ = cmdinfo.get('stdin')
|
|
stdin = None
|
|
if(stdin_templ and len(stdin_templ)):
|
|
stdin_full = list()
|
|
for piece in stdin_templ:
|
|
if(params_replacements and len(params_replacements)):
|
|
stdin_full.append(param_replace(piece, params_replacements,
|
|
ignore_missing=ignore_missing))
|
|
else:
|
|
stdin_full.append(piece)
|
|
stdin = joinlinesep(*stdin_full)
|
|
root_run = cmdinfo.get('run_as_root', False)
|
|
sh.execute(*cmd_to_run, run_as_root=root_run, process_input=stdin, **kargs)
|
|
|
|
|
|
def fetch_dependencies(component, add=False):
|
|
if(add):
|
|
deps = list([component])
|
|
else:
|
|
deps = list()
|
|
cdeps = get_dependencies(component)
|
|
if(cdeps and len(cdeps)):
|
|
for d in cdeps:
|
|
deps = deps + fetch_dependencies(d, True)
|
|
return deps
|
|
|
|
|
|
def prioritize_components(components):
|
|
#get the right component order (by priority)
|
|
mporder = dict()
|
|
priorities = constants.COMPONENT_NAMES_PRIORITY
|
|
for c in components:
|
|
priority = priorities.get(c)
|
|
if(priority == None):
|
|
priority = sys.maxint
|
|
mporder[c] = priority
|
|
#sort by priority value
|
|
priority_order = sorted(mporder.iteritems(), key=operator.itemgetter(1))
|
|
#extract the right order
|
|
component_order = [x[0] for x in priority_order]
|
|
return component_order
|
|
|
|
|
|
def component_paths(root, component_name):
|
|
component_root = sh.joinpths(root, component_name)
|
|
tracedir = sh.joinpths(component_root, constants.COMPONENT_TRACE_DIR)
|
|
appdir = sh.joinpths(component_root, constants.COMPONENT_APP_DIR)
|
|
cfgdir = sh.joinpths(component_root, constants.COMPONENT_CONFIG_DIR)
|
|
return (component_root, tracedir, appdir, cfgdir)
|
|
|
|
|
|
def load_json(fn):
|
|
data = sh.load_file(fn)
|
|
lines = data.splitlines()
|
|
new_lines = list()
|
|
for line in lines:
|
|
if(line.lstrip().startswith('#')):
|
|
continue
|
|
new_lines.append(line)
|
|
data = joinlinesep(*new_lines)
|
|
return json.loads(data)
|
|
|
|
|
|
def get_host_ip(cfg=None):
|
|
ip = None
|
|
if(cfg):
|
|
cfg_ip = cfg.get('default', 'host_ip')
|
|
if(cfg_ip and len(cfg_ip)):
|
|
ip = cfg_ip
|
|
if(ip == None):
|
|
interfaces = get_interfaces()
|
|
def_info = interfaces.get(constants.DEFAULT_NET_INTERFACE)
|
|
if(def_info):
|
|
ipinfo = def_info.get(constants.DEFAULT_NET_INTERFACE_IP_VERSION)
|
|
if(ipinfo):
|
|
ip = ipinfo.get('addr')
|
|
if(ip == None):
|
|
msg = "Your host does not have an ip address!"
|
|
raise excp.NoIpException(msg)
|
|
LOG.debug("Determined host ip to be: \"%s\"" % (ip))
|
|
return ip
|
|
|
|
|
|
def get_interfaces():
|
|
interfaces = dict()
|
|
for intfc in netifaces.interfaces():
|
|
interface_info = dict()
|
|
interface_addresses = netifaces.ifaddresses(intfc)
|
|
ip6 = interface_addresses.get(netifaces.AF_INET6)
|
|
if(ip6 and len(ip6)):
|
|
#just take the first
|
|
interface_info[constants.IPV6] = ip6[0]
|
|
ip4 = interface_addresses.get(netifaces.AF_INET)
|
|
if(ip4 and len(ip4)):
|
|
#just take the first
|
|
interface_info[constants.IPV4] = ip4[0]
|
|
#there are others but this is good for now
|
|
interfaces[intfc] = interface_info
|
|
return interfaces
|
|
|
|
|
|
def determine_distro():
|
|
plt = platform.platform()
|
|
#ensure its a linux distro
|
|
(distname, version, id) = platform.linux_distribution()
|
|
if(not distname):
|
|
return (None, plt)
|
|
#attempt to match it to our platforms
|
|
found_os = None
|
|
for (known_os, pattern) in constants.KNOWN_DISTROS.items():
|
|
if(pattern.search(plt)):
|
|
found_os = known_os
|
|
break
|
|
return (found_os, plt)
|
|
|
|
|
|
def get_pip_list(distro, component):
|
|
LOG.info("Getting pip packages for distro %s and component %s." % (distro, component))
|
|
all_pkgs = dict()
|
|
fns = constants.PIP_MAP.get(component)
|
|
if(fns == None):
|
|
return all_pkgs
|
|
#load + merge them
|
|
for fn in fns:
|
|
js = load_json(fn)
|
|
distro_pkgs = js.get(distro)
|
|
if(distro_pkgs and len(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
|
|
|
|
|
|
def get_pkg_list(distro, component):
|
|
LOG.info("Getting packages for distro %s and component %s." % (distro, component))
|
|
all_pkgs = dict()
|
|
fns = constants.PKG_MAP.get(component)
|
|
if(fns == None):
|
|
return all_pkgs
|
|
#load + merge them
|
|
for fn in fns:
|
|
js = load_json(fn)
|
|
distro_pkgs = js.get(distro)
|
|
if(distro_pkgs and len(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 == constants.PRE_INSTALL or infokey == constants.POST_INSTALL):
|
|
oldinstalllist = oldpkginfo.get(infokey) or []
|
|
infovalue = oldinstalllist + infovalue
|
|
newpkginfo[infokey] = infovalue
|
|
combined[pkgname] = newpkginfo
|
|
else:
|
|
combined[pkgname] = pkginfo
|
|
all_pkgs = combined
|
|
return all_pkgs
|
|
|
|
|
|
def joinlinesep(*pieces):
|
|
return os.linesep.join(pieces)
|
|
|
|
|
|
def param_replace(text, replacements, ignore_missing=False):
|
|
|
|
if(not replacements or len(replacements) == 0):
|
|
return text
|
|
|
|
if(len(text) == 0):
|
|
return text
|
|
|
|
if(ignore_missing):
|
|
LOG.debug("Performing parameter replacements (ignoring missing) on %s" % (text))
|
|
else:
|
|
LOG.debug("Performing parameter replacements (not ignoring missing) on %s" % (text))
|
|
|
|
def replacer(match):
|
|
org = match.group(0)
|
|
name = match.group(1)
|
|
v = replacements.get(name)
|
|
if(v == None and ignore_missing):
|
|
v = org
|
|
elif(v == None and not ignore_missing):
|
|
msg = "No replacement found for parameter %s" % (org)
|
|
raise excp.NoReplacementException(msg)
|
|
else:
|
|
LOG.debug("Replacing [%s] with [%s]" % (org, str(v)))
|
|
return str(v)
|
|
|
|
return PARAM_SUB_REGEX.sub(replacer, text)
|
|
|
|
|
|
|
|
def welcome(program_action):
|
|
formatted_action = constants.WELCOME_MAP.get(program_action, "")
|
|
ver_str = version.version_string()
|
|
lower = "!%s %s!" % (formatted_action.upper(), ver_str)
|
|
welcome = r'''
|
|
___ ____ _____ _ _ ____ _____ _ ____ _ __
|
|
/ _ \| _ \| ____| \ | / ___|_ _|/ \ / ___| |/ /
|
|
| | | | |_) | _| | \| \___ \ | | / _ \| | | ' /
|
|
| |_| | __/| |___| |\ |___) || |/ ___ \ |___| . \
|
|
\___/|_| |_____|_| \_|____/ |_/_/ \_\____|_|\_\
|
|
|
|
'''
|
|
#this seems needed, weird...
|
|
welcome = " " + welcome.strip()
|
|
max_len = 0
|
|
for line in welcome.splitlines():
|
|
if(len(line) > max_len):
|
|
max_len = len(line)
|
|
lower_out = colored(constants.PROG_NICE_NAME, 'green') + \
|
|
": " + colored(lower, 'blue')
|
|
center_len = (max_len + max_len/3)
|
|
lower_out = string.center(lower_out, center_len)
|
|
msg = welcome + os.linesep + lower_out
|
|
print(msg)
|