136 lines
5.0 KiB
Python
136 lines
5.0 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 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(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)
|
|
for k in run_over_keys:
|
|
if quiet:
|
|
root = root.get(k)
|
|
if root is None:
|
|
return None
|
|
else:
|
|
root = root[k]
|
|
if not quiet:
|
|
return root[end_key]
|
|
else:
|
|
return root.get(end_key)
|
|
|
|
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_packager_factory(self):
|
|
"""Return a factory for a package manager."""
|
|
return importer.import_entry_point(self._packager_name)
|
|
|
|
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]
|
|
cls = importer.import_entry_point(entry_point)
|
|
# Knock all action class info (and any other keys)
|
|
key_deletions = [action] + settings.ACTIONS
|
|
for k in key_deletions:
|
|
if k in component_info:
|
|
del component_info[k]
|
|
return (cls, component_info)
|
|
except KeyError:
|
|
raise RuntimeError('No class configured to %s %s on %s' %
|
|
(action, name, self.name))
|