A class that knows how to read a YAML file and manipulate the system in a distribution-specific way based on the instructions found there.
This commit is contained in:
parent
01b2b8c689
commit
b1f372e8f6
106
devstack/distro.py
Normal file
106
devstack/distro.py
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
import glob
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import re
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
from devstack import importer
|
||||||
|
from devstack import log as logging
|
||||||
|
from devstack import settings
|
||||||
|
from devstack import utils
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger('devstack.distro')
|
||||||
|
|
||||||
|
DISTRO_CONF_DIR = os.path.join(settings.STACK_CONFIG_DIR, 'distros')
|
||||||
|
|
||||||
|
|
||||||
|
class Distro(object):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load_all(cls, path=DISTRO_CONF_DIR):
|
||||||
|
"""Returns a list of the known distros."""
|
||||||
|
results = []
|
||||||
|
input_files = glob.glob(os.path.join(DISTRO_CONF_DIR, '*.yaml'))
|
||||||
|
if not input_files:
|
||||||
|
raise RuntimeError(
|
||||||
|
'Did not find any distro definition files in %s' %
|
||||||
|
DISTRO_CONF_DIR)
|
||||||
|
for filename in input_files:
|
||||||
|
try:
|
||||||
|
with open(filename, 'r') as f:
|
||||||
|
data = yaml.load(f)
|
||||||
|
results.append(cls(**data))
|
||||||
|
except Exception as err:
|
||||||
|
LOG.warning('Could not load distro definition from %s: %s',
|
||||||
|
filename, 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 "%s"', p.name, plt)
|
||||||
|
return p
|
||||||
|
else:
|
||||||
|
raise RuntimeError(
|
||||||
|
'No platform configuration data for %s (%s)' %
|
||||||
|
(plt, distname))
|
||||||
|
|
||||||
|
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 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 get_component_action_class(self, name, action):
|
||||||
|
"""Return the class to use for doing the action w/the component."""
|
||||||
|
try:
|
||||||
|
entry_point = self.components[name][action]
|
||||||
|
except KeyError:
|
||||||
|
raise RuntimeError('No class configured to %s %s on %s' %
|
||||||
|
(action, name, self.name))
|
||||||
|
return importer.import_entry_point(entry_point)
|
||||||
|
|
||||||
|
def resolve_component_dependencies(self, components):
|
||||||
|
"""Returns list of all components needed for the named components."""
|
||||||
|
all_components = {}
|
||||||
|
active_names = [(c, None) for c in components]
|
||||||
|
while active_names:
|
||||||
|
component, parent = active_names.pop()
|
||||||
|
try:
|
||||||
|
component_details = self.components[component]
|
||||||
|
except KeyError:
|
||||||
|
if parent:
|
||||||
|
raise RuntimeError(
|
||||||
|
'Could not find details about component %r, a dependency of %s, for %s' %
|
||||||
|
(component, parent, self.name))
|
||||||
|
else:
|
||||||
|
raise RuntimeError(
|
||||||
|
'Could not find details about component %r for %s' %
|
||||||
|
(component, self.name))
|
||||||
|
deps = set(component_details.get('dependencies', []))
|
||||||
|
all_components[component] = deps
|
||||||
|
for d in deps:
|
||||||
|
if d not in all_components and d not in active_names:
|
||||||
|
active_names.append((d, component))
|
||||||
|
return all_components
|
14
tests/test_distro.py
Normal file
14
tests/test_distro.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
from devstack import distro
|
||||||
|
|
||||||
|
|
||||||
|
def test_component_dependencies():
|
||||||
|
d = distro.Distro('fake', 'ignore', 'apt', {},
|
||||||
|
{'a': {'dependencies': ['b'],
|
||||||
|
},
|
||||||
|
'b': {},
|
||||||
|
})
|
||||||
|
actual = d.resolve_component_dependencies(['a'])
|
||||||
|
assert actual == {'a': set(['b']),
|
||||||
|
'b': set(),
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user