679f33b242
Have the component get a packager for each package and use that, falling back to a default provided by the distro. This will allow us to override the packaging rules for specific packages by providing a class reference in the distro file.
149 lines
5.5 KiB
Python
149 lines
5.5 KiB
Python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
|
|
# Copyright (C) 2012 Yahoo! Inc. All Rights Reserved.
|
|
# Copyright (C) 2012 New Dream Network, LLC (DreamHost) 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 glob
|
|
import platform
|
|
import re
|
|
import shlex
|
|
|
|
import yaml
|
|
|
|
from devstack import decorators
|
|
from devstack import importer
|
|
from devstack import log as logging
|
|
from devstack import settings
|
|
from devstack import shell as sh
|
|
|
|
LOG = logging.getLogger('devstack.distro')
|
|
|
|
|
|
class Distro(object):
|
|
|
|
@classmethod
|
|
def load_all(cls, path=settings.STACK_DISTRO_DIR):
|
|
"""Returns a list of the known distros."""
|
|
results = []
|
|
input_files = glob.glob(sh.joinpths(path, '*.yaml'))
|
|
if not input_files:
|
|
raise RuntimeError(
|
|
'Did not find any distro definition files in %s' %
|
|
path)
|
|
for fn in input_files:
|
|
cls_kvs = None
|
|
filename = sh.abspth(fn)
|
|
LOG.audit("Attempting to load distro definition from [%s]" % (filename))
|
|
try:
|
|
with open(filename, 'r') as f:
|
|
cls_kvs = yaml.load(f)
|
|
except (IOError, yaml.YAMLError) as err:
|
|
LOG.warning('Could not load distro definition from %s: %s',
|
|
filename, err)
|
|
if cls_kvs is not None:
|
|
try:
|
|
results.append(cls(**cls_kvs))
|
|
except Exception as err:
|
|
LOG.warning('Could not initialize instance %s using parameter map %s: %s',
|
|
cls, cls_kvs, err)
|
|
return results
|
|
|
|
@classmethod
|
|
def get_current(cls):
|
|
"""Returns a Distro instance configured for the current system."""
|
|
plt = platform.platform()
|
|
distname = platform.linux_distribution()[0]
|
|
if not distname:
|
|
raise RuntimeError('Unsupported platform %s' % plt)
|
|
LOG.debug('Looking for distro data for %s (%s)', plt, distname)
|
|
for p in cls.load_all():
|
|
if p.supports_distro(plt):
|
|
LOG.info('Using distro "%s" for platform "%s"', p.name, plt)
|
|
return p
|
|
else:
|
|
raise RuntimeError(
|
|
'No platform configuration data for %s (%s)' %
|
|
(plt, distname))
|
|
|
|
@decorators.log_debug
|
|
def __init__(self, name, distro_pattern, packager_name, commands, components):
|
|
self.name = name
|
|
self._distro_pattern = re.compile(distro_pattern, re.IGNORECASE)
|
|
self._packager_name = packager_name
|
|
self._commands = commands
|
|
self._components = components
|
|
|
|
def get_command_config(self, key, *more_keys, **kargs):
|
|
""" Gets a end object for a given set of keys """
|
|
root = self._commands
|
|
acutal_keys = [key] + list(more_keys)
|
|
run_over_keys = acutal_keys[0:-1]
|
|
end_key = acutal_keys[-1]
|
|
quiet = kargs.get('quiet', False)
|
|
LOG.debug("Running over keys (%s)" % (", ".join(run_over_keys)))
|
|
LOG.debug("End key is (%s)" % (end_key))
|
|
for k in run_over_keys:
|
|
if quiet:
|
|
root = root.get(k)
|
|
if root is None:
|
|
return None
|
|
else:
|
|
root = root[k]
|
|
end_value = None
|
|
if not quiet:
|
|
end_value = root[end_key]
|
|
else:
|
|
end_value = root.get(end_key)
|
|
LOG.debug("Retrieved end command config: %s", end_value)
|
|
return end_value
|
|
|
|
def get_command(self, key, *more_keys, **kargs):
|
|
"""Retrieves a string for running a command from the setup
|
|
and splits it to return a list.
|
|
"""
|
|
val = self.get_command_config(key, *more_keys, **kargs)
|
|
ret_val = shlex.split(val) if val else []
|
|
LOG.debug("Parsed configured command: %s", ret_val)
|
|
return ret_val
|
|
|
|
def known_component(self, name):
|
|
return name in self._components
|
|
|
|
def supports_distro(self, distro_name):
|
|
"""Does this distro support the named Linux distro?
|
|
|
|
:param distro_name: Return value from platform.linux_distribution().
|
|
"""
|
|
return bool(self._distro_pattern.search(distro_name))
|
|
|
|
def get_default_package_manager(self):
|
|
"""Return a package manager that will work for this distro."""
|
|
return importer.import_entry_point(self._packager_name)(self)
|
|
|
|
def extract_component(self, name, action):
|
|
"""Return the class + component info to use for doing the action w/the component."""
|
|
try:
|
|
# Use a copy instead of the original
|
|
component_info = dict(self._components[name])
|
|
entry_point = component_info['action_classes'][action]
|
|
cls = importer.import_entry_point(entry_point)
|
|
# Remove action class info
|
|
if 'action_classes' in component_info:
|
|
del component_info['action_classes']
|
|
return (cls, component_info)
|
|
except KeyError:
|
|
raise RuntimeError('No class configured to %s %s on %s' %
|
|
(action, name, self.name))
|