Merge branch 'master' into ssh_callback

This commit is contained in:
Nico Schottelius 2013-05-17 08:41:09 +02:00
commit 22874bfa84
43 changed files with 496 additions and 366 deletions

2
Makefile Normal file
View file

@ -0,0 +1,2 @@
%:
./build $@

View file

@ -19,7 +19,7 @@
#
owner="$(cat "$__object/parameter/owner" 2>/dev/null || echo "$__object_id")"
state="$(cat "$__object/parameter/present" 2>/dev/null || echo "present")"
state="$(cat "$__object/parameter/state" 2>/dev/null || echo "present")"
if [ -f "$__object/parameter/file" ]; then
file="$(cat "$__object/parameter/file")"
else

View file

@ -1,7 +1,7 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# 2010-2012 Nico Schottelius (nico-cdist at schottelius.org)
# 2010-2013 Nico Schottelius (nico-cdist at schottelius.org)
#
# This file is part of cdist.
#
@ -22,18 +22,12 @@
import logging
import os
import stat
import shutil
import sys
import tempfile
import time
import itertools
import pprint
import cdist
from cdist import core
from cdist import resolver
class ConfigInstall(object):
"""Cdist main class to hold arbitrary data"""
@ -63,38 +57,77 @@ class ConfigInstall(object):
shutil.rmtree(destination)
shutil.move(self.context.local.out_path, destination)
def deploy_to(self):
"""Mimic the old deploy to: Deploy to one host"""
self.stage_prepare()
self.stage_run()
def deploy_and_cleanup(self):
def run(self):
"""Do what is most often done: deploy & cleanup"""
start_time = time.time()
self.deploy_to()
self.cleanup()
self.log.info("Finished successful run in %s seconds",
time.time() - start_time)
def stage_prepare(self):
"""Do everything for a deploy, minus the actual code stage"""
self.explorer.run_global_explorers(self.context.local.global_explorer_out_path)
self.manifest.run_initial_manifest(self.context.initial_manifest)
self.iterate_until_finished()
self.log.info("Running object manifests and type explorers")
self.cleanup()
self.log.info("Finished successful run in %s seconds", time.time() - start_time)
# Continue process until no new objects are created anymore
new_objects_created = True
while new_objects_created:
new_objects_created = False
for cdist_object in core.CdistObject.list_objects(self.context.local.object_path,
def object_list(self):
"""Short name for object list retrieval"""
for cdist_object in core.CdistObject.list_objects(self.context.local.object_path,
self.context.local.type_path):
if cdist_object.state == core.CdistObject.STATE_PREPARED:
self.log.debug("Skipping re-prepare of object %s", cdist_object)
yield cdist_object
def iterate_until_finished(self):
# Continue process until no new objects are created anymore
objects_changed = True
while objects_changed:
objects_changed = False
for cdist_object in self.object_list():
if cdist_object.requirements_unfinished(cdist_object.requirements):
"""We cannot do anything for this poor object"""
continue
else:
if cdist_object.state == core.CdistObject.STATE_UNDEF:
"""Prepare the virgin object"""
self.object_prepare(cdist_object)
new_objects_created = True
objects_changed = True
if cdist_object.requirements_unfinished(cdist_object.autorequire):
"""The previous step created objects we depend on - wait for them"""
continue
if cdist_object.state == core.CdistObject.STATE_PREPARED:
self.object_run(cdist_object)
objects_changed = True
# Check whether all objects have been finished
unfinished_objects = []
for cdist_object in self.object_list():
if not cdist_object.state == cdist_object.STATE_DONE:
unfinished_objects.append(cdist_object)
if unfinished_objects:
info_string = []
for cdist_object in unfinished_objects:
requirement_names = []
autorequire_names = []
for requirement in cdist_object.requirements_unfinished(cdist_object.requirements):
requirement_names.append(requirement.name)
for requirement in cdist_object.requirements_unfinished(cdist_object.autorequire):
autorequire_names.append(requirement.name)
requirements = ", ".join(requirement_names)
autorequire = ", ".join(autorequire_names)
info_string.append("%s requires: %s autorequires: %s" % (cdist_object.name, requirements, autorequire))
raise cdist.Error("The requirements of the following objects could not be resolved: %s" %
("; ".join(info_string)))
def object_prepare(self, cdist_object):
"""Prepare object: Run type explorer + manifest"""
@ -129,18 +162,3 @@ class ConfigInstall(object):
# Mark this object as done
self.log.debug("Finishing run of " + cdist_object.name)
cdist_object.state = core.CdistObject.STATE_DONE
def stage_run(self):
"""The final (and real) step of deployment"""
self.log.info("Generating and executing code")
objects = core.CdistObject.list_objects(
self.context.local.object_path,
self.context.local.type_path)
dependency_resolver = resolver.DependencyResolver(objects)
self.log.debug(pprint.pformat(dependency_resolver.dependencies))
for cdist_object in dependency_resolver:
self.log.debug("Run object: %s", cdist_object)
self.object_run(cdist_object)

View file

@ -50,7 +50,6 @@ class MissingObjectIdError(cdist.Error):
def __str__(self):
return '%s' % (self.message)
class CdistObject(object):
"""Represents a cdist object.
@ -61,6 +60,7 @@ class CdistObject(object):
"""
# Constants for use with Object.state
STATE_UNDEF = ""
STATE_PREPARED = "prepared"
STATE_RUNNING = "running"
STATE_DONE = "done"
@ -223,62 +223,18 @@ class CdistObject(object):
except EnvironmentError as error:
raise cdist.Error('Error creating directories for cdist object: %s: %s' % (self, error))
@property
def satisfied_requirements(self):
"""Return state whether all of our dependencies have been resolved already"""
def requirements_unfinished(self, requirements):
"""Return state whether requirements are satisfied"""
satisfied = True
object_list = []
for requirement in self.all_requirements:
log.debug("%s: Checking requirement %s (%s) .." % (self.name, requirement.name, requirement.state))
if not requirement.state == self.STATE_DONE:
satisfied = False
break
log.debug("%s is satisfied: %s" % (self.name, satisfied))
for requirement in requirements:
cdist_object = self.object_from_name(requirement)
return satisfied
def find_requirements_by_name(self, requirements):
"""Takes a list of requirement patterns and returns a list of matching object instances.
Patterns are expected to be Unix shell-style wildcards for use with fnmatch.filter.
find_requirements_by_name(['__type/object_id', '__other_type/*']) ->
[<Object __type/object_id>, <Object __other_type/any>, <Object __other_type/match>]
"""
# FIXME: think about where/when to store this - probably not here
self.objects = dict((o.name, o) for o in self.list_objects(self.base_path, self.cdist_type.base_path))
object_names = self.objects.keys()
for pattern in requirements:
found = False
for requirement in fnmatch.filter(object_names, pattern):
found = True
yield self.objects[requirement]
if not found:
# FIXME: get rid of the singleton object_id, it should be invisible to the code -> hide it in Object
singleton = os.path.join(pattern, 'singleton')
if singleton in self.objects:
yield self.objects[singleton]
else:
raise RequirementNotFoundError(pattern)
@property
def all_requirements(self):
"""
Return resolved autorequirements and requirements so that
a complete list of requirements is returned
"""
all_reqs= []
all_reqs.extend(self.find_requirements_by_name(self.requirements))
all_reqs.extend(self.find_requirements_by_name(self.autorequire))
return set(all_reqs)
if not cdist_object.state == self.STATE_DONE:
object_list.append(cdist_object)
return object_list
class RequirementNotFoundError(cdist.Error):
def __init__(self, requirement):

View file

@ -1,175 +0,0 @@
# -*- coding: utf-8 -*-
#
# 2011 Steven Armstrong (steven-cdist at armstrong.cc)
#
# This file is part of cdist.
#
# cdist is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# cdist is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with cdist. If not, see <http://www.gnu.org/licenses/>.
#
#
import logging
import os
import itertools
import fnmatch
import pprint
import cdist
log = logging.getLogger(__name__)
class CircularReferenceError(cdist.Error):
def __init__(self, cdist_object, required_object):
self.cdist_object = cdist_object
self.required_object = required_object
def __str__(self):
return 'Circular reference detected: %s -> %s' % (self.cdist_object.name, self.required_object.name)
class RequirementNotFoundError(cdist.Error):
def __init__(self, requirement):
self.requirement = requirement
def __str__(self):
return 'Requirement could not be found: %s' % self.requirement
class DependencyResolver(object):
"""Cdist's dependency resolver.
Usage:
>> resolver = DependencyResolver(list_of_objects)
# Easy access to the objects we are working with
>> resolver.objects['__some_type/object_id']
<CdistObject __some_type/object_id>
# Easy access to a specific objects dependencies
>> resolver.dependencies['__some_type/object_id']
[<CdistObject __other_type/dependency>, <CdistObject __some_type/object_id>]
# Pretty print the dependency graph
>> from pprint import pprint
>> pprint(resolver.dependencies)
# Iterate over all existing objects in the correct order
>> for cdist_object in resolver:
>> do_something_with(cdist_object)
"""
def __init__(self, objects, logger=None):
self.objects = dict((o.name, o) for o in objects)
self._dependencies = None
self.log = logger or log
@property
def dependencies(self):
"""Build the dependency graph.
Returns a dict where the keys are the object names and the values are
lists of all dependencies including the key object itself.
"""
if self._dependencies is None:
self.log.info("Resolving dependencies...")
self._dependencies = {}
self._preprocess_requirements()
for name,cdist_object in self.objects.items():
resolved = []
unresolved = []
self._resolve_object_dependencies(cdist_object, resolved, unresolved)
self._dependencies[name] = resolved
self.log.debug(self._dependencies)
return self._dependencies
def find_requirements_by_name(self, requirements):
"""Takes a list of requirement patterns and returns a list of matching object instances.
Patterns are expected to be Unix shell-style wildcards for use with fnmatch.filter.
find_requirements_by_name(['__type/object_id', '__other_type/*']) ->
[<Object __type/object_id>, <Object __other_type/any>, <Object __other_type/match>]
"""
object_names = self.objects.keys()
for pattern in requirements:
found = False
for requirement in fnmatch.filter(object_names, pattern):
found = True
yield self.objects[requirement]
if not found:
# FIXME: get rid of the singleton object_id, it should be invisible to the code -> hide it in Object
singleton = os.path.join(pattern, 'singleton')
if singleton in self.objects:
yield self.objects[singleton]
else:
raise RequirementNotFoundError(pattern)
def _preprocess_requirements(self):
"""Find all autorequire dependencies and merge them to be just requirements
for further processing.
"""
for cdist_object in self.objects.values():
if cdist_object.autorequire:
# The objects (children) that this cdist_object (parent) defined
# in it's type manifest shall inherit all explicit requirements
# that the parent has so that user defined requirements are
# fullfilled and processed in the expected order.
for auto_requirement in self.find_requirements_by_name(cdist_object.autorequire):
for requirement in self.find_requirements_by_name(cdist_object.requirements):
requirement_object_all_requirements = list(requirement.requirements) + list(requirement.autorequire)
if (requirement.name not in auto_requirement.requirements
and auto_requirement.name not in requirement_object_all_requirements):
self.log.debug('Adding %s to %s.requirements', requirement.name, auto_requirement)
auto_requirement.requirements.append(requirement.name)
# On the other hand the parent shall depend on all the children
# it created so that the user can setup dependencies on it as a
# whole without having to know anything about the parents
# internals.
cdist_object.requirements.extend(cdist_object.autorequire)
# As we changed the object on disc, we have to ensure it is not
# preprocessed again if someone would call us multiple times.
cdist_object.autorequire = []
def _resolve_object_dependencies(self, cdist_object, resolved, unresolved):
"""Resolve all dependencies for the given cdist_object and store them
in the list which is passed as the 'resolved' arguments.
e.g.
resolved = []
unresolved = []
resolve_object_dependencies(some_object, resolved, unresolved)
print("Dependencies for %s: %s" % (some_object, resolved))
"""
self.log.debug('Resolving dependencies for: %s' % cdist_object.name)
try:
unresolved.append(cdist_object)
for required_object in self.find_requirements_by_name(cdist_object.requirements):
self.log.debug("Object %s requires %s", cdist_object, required_object)
if required_object not in resolved:
if required_object in unresolved:
error = CircularReferenceError(cdist_object, required_object)
self.log.error('%s: %s', error, pprint.pformat(self._dependencies))
raise error
self._resolve_object_dependencies(required_object, resolved, unresolved)
resolved.append(cdist_object)
unresolved.remove(cdist_object)
except RequirementNotFoundError as e:
raise cdist.CdistObjectError(cdist_object, "requires non-existing " + e.requirement)
def __iter__(self):
"""Iterate over all unique objects and yield them in the correct order.
"""
iterable = itertools.chain(*self.dependencies.values())
# Keep record of objects that have already been seen
seen = set()
seen_add = seen.add
for cdist_object in itertools.filterfalse(seen.__contains__, iterable):
seen_add(cdist_object)
yield cdist_object

View file

@ -1,91 +0,0 @@
# -*- coding: utf-8 -*-
#
# 2010-2011 Steven Armstrong (steven-cdist at armstrong.cc)
# 2012 Nico Schottelius (nico-cdist at schottelius.org)
#
# This file is part of cdist.
#
# cdist is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# cdist is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with cdist. If not, see <http://www.gnu.org/licenses/>.
#
#
import os
import shutil
import cdist
from cdist import test
from cdist.exec import local
from cdist import core
from cdist.core import manifest
from cdist import resolver
from cdist import config
import cdist.context
import os.path as op
my_dir = op.abspath(op.dirname(__file__))
fixtures = op.join(my_dir, 'fixtures')
add_conf_dir = op.join(fixtures, 'conf')
class AutorequireTestCase(test.CdistTestCase):
def setUp(self):
self.orig_environ = os.environ
os.environ = os.environ.copy()
self.temp_dir = self.mkdtemp()
self.out_dir = os.path.join(self.temp_dir, "out")
self.remote_out_dir = os.path.join(self.temp_dir, "remote")
os.environ['__cdist_out_dir'] = self.out_dir
os.environ['__cdist_remote_out_dir'] = self.remote_out_dir
self.context = cdist.context.Context(
target_host=self.target_host,
remote_copy=self.remote_copy,
remote_exec=self.remote_exec,
add_conf_dirs=[add_conf_dir],
exec_path=test.cdist_exec_path,
debug=False)
self.config = config.Config(self.context)
def tearDown(self):
os.environ = self.orig_environ
shutil.rmtree(self.temp_dir)
def test_implicit_dependencies(self):
self.context.initial_manifest = os.path.join(self.context.local.manifest_path, 'implicit_dependencies')
self.config.stage_prepare()
objects = core.CdistObject.list_objects(self.context.local.object_path, self.context.local.type_path)
dependency_resolver = resolver.DependencyResolver(objects)
expected_dependencies = [
dependency_resolver.objects['__package_special/b'],
dependency_resolver.objects['__package/b'],
dependency_resolver.objects['__package_special/a']
]
resolved_dependencies = dependency_resolver.dependencies['__package_special/a']
self.assertEqual(resolved_dependencies, expected_dependencies)
def test_circular_dependency(self):
self.context.initial_manifest = os.path.join(self.context.local.manifest_path, 'circular_dependency')
self.config.stage_prepare()
# raises CircularDependecyError
self.config.stage_run()
def test_recursive_type(self):
self.context.initial_manifest = os.path.join(self.config.local.manifest_path, 'recursive_type')
self.config.stage_prepare()
# raises CircularDependecyError
self.config.stage_run()

View file

@ -86,3 +86,94 @@ class ResolverTestCase(test.CdistTestCase):
first_man.requirements = ['__does/not/exist']
with self.assertRaises(cdist.Error):
self.dependency_resolver.dependencies
# -*- coding: utf-8 -*-
#
# 2010-2011 Steven Armstrong (steven-cdist at armstrong.cc)
# 2012 Nico Schottelius (nico-cdist at schottelius.org)
#
# This file is part of cdist.
#
# cdist is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# cdist is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with cdist. If not, see <http://www.gnu.org/licenses/>.
#
#
import os
import shutil
import cdist
from cdist import test
from cdist.exec import local
from cdist import core
from cdist.core import manifest
from cdist import resolver
from cdist import config
import cdist.context
import os.path as op
my_dir = op.abspath(op.dirname(__file__))
fixtures = op.join(my_dir, 'fixtures')
add_conf_dir = op.join(fixtures, 'conf')
class AutorequireTestCase(test.CdistTestCase):
def setUp(self):
self.orig_environ = os.environ
os.environ = os.environ.copy()
self.temp_dir = self.mkdtemp()
self.out_dir = os.path.join(self.temp_dir, "out")
self.remote_out_dir = os.path.join(self.temp_dir, "remote")
os.environ['__cdist_out_dir'] = self.out_dir
os.environ['__cdist_remote_out_dir'] = self.remote_out_dir
self.context = cdist.context.Context(
target_host=self.target_host,
remote_copy=self.remote_copy,
remote_exec=self.remote_exec,
add_conf_dirs=[add_conf_dir],
exec_path=test.cdist_exec_path,
debug=False)
self.config = config.Config(self.context)
def tearDown(self):
os.environ = self.orig_environ
shutil.rmtree(self.temp_dir)
def test_implicit_dependencies(self):
self.context.initial_manifest = os.path.join(self.context.local.manifest_path, 'implicit_dependencies')
self.config.stage_prepare()
objects = core.CdistObject.list_objects(self.context.local.object_path, self.context.local.type_path)
dependency_resolver = resolver.DependencyResolver(objects)
expected_dependencies = [
dependency_resolver.objects['__package_special/b'],
dependency_resolver.objects['__package/b'],
dependency_resolver.objects['__package_special/a']
]
resolved_dependencies = dependency_resolver.dependencies['__package_special/a']
self.assertEqual(resolved_dependencies, expected_dependencies)
def test_circular_dependency(self):
self.context.initial_manifest = os.path.join(self.context.local.manifest_path, 'circular_dependency')
self.config.stage_prepare()
# raises CircularDependecyError
self.config.stage_run()
def test_recursive_type(self):
self.context.initial_manifest = os.path.join(self.config.local.manifest_path, 'recursive_type')
self.config.stage_prepare()
# raises CircularDependecyError
self.config.stage_run()

View file

@ -6,6 +6,7 @@ Changelog
next:
* Core: Make global explorers available to initial manifest (Arkaitz Jimenez)
* Change execution order to run object as one unit
2.1.1: 2013-04-08
* Core: Use dynamic dependency resolver to allow indirect self dependencies

Binary file not shown.

View file

@ -0,0 +1,282 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="1052.3622"
height="744.09448"
id="svg2"
version="1.1"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="cdist-sexy-actions.svg"
inkscape:export-filename="/home/users/nico/cdist-sexy-actions.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.62488877"
inkscape:cx="526.18109"
inkscape:cy="410.90353"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
objecttolerance="20"
guidetolerance="20"
inkscape:window-width="1436"
inkscape:window-height="861"
inkscape:window-x="0"
inkscape:window-y="18"
inkscape:window-maximized="0"
gridtolerance="10" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-308.2677)">
<g
id="g3791"
transform="translate(-65.448375,393.5891)">
<rect
y="167.46855"
x="222.23357"
height="88.893425"
width="173.74623"
id="rect2985"
style="fill:#cdff13;fill-opacity:0.90416715;stroke:none" />
<text
sodipodi:linespacing="125%"
id="text3755"
y="228.0777"
x="260.61935"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
y="228.0777"
x="260.61935"
id="tspan3757"
sodipodi:role="line">cdist</tspan></text>
</g>
<g
id="g3802"
transform="translate(-88.702304,-97.993841)">
<rect
y="519.00165"
x="109.09647"
height="90.913727"
width="452.54834"
id="rect3796"
style="fill:#008000;fill-opacity:0.90416715;stroke:none" />
<text
sodipodi:linespacing="125%"
id="text3798"
y="575.57019"
x="171.72594"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
y="575.57019"
x="171.72594"
id="tspan3800"
sodipodi:role="line">configures hosts</tspan></text>
</g>
<g
id="g3791-5"
transform="translate(258.6201,492.81494)">
<g
id="g3834"
transform="translate(204.05081,-98.994949)">
<rect
style="fill:#cdff13;fill-opacity:0.90416715;stroke:none"
id="rect2985-2"
width="173.74623"
height="88.893425"
x="222.23357"
y="167.46855" />
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="264.65994"
y="224.03709"
id="text3755-9"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3757-2"
x="264.65994"
y="224.03709">sexy</tspan></text>
</g>
</g>
<g
id="g3802-3"
transform="translate(436.48671,-101.85286)">
<g
id="g3866">
<rect
style="fill:#008000;fill-opacity:0.90416715;stroke:none"
id="rect3796-9"
width="452.54834"
height="90.913727"
x="109.09647"
y="519.00165" />
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="143.44167"
y="573.54987"
id="text3798-6"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3800-5"
x="143.44167"
y="573.54987">manages inventory</tspan></text>
</g>
</g>
<g
id="g3802-9"
transform="matrix(0.96624748,0,0,1,-134.02038,188.43537)">
<g
id="g3980"
transform="translate(112.6206,22.403987)">
<rect
style="fill:#822a0e;fill-opacity:1;stroke:none"
id="rect3796-1"
width="319.27777"
height="146.92369"
x="115.72122"
y="536.6048" />
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="269.50381"
y="592.71771"
id="text3798-3"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3800-6"
x="269.50381"
y="592.71771">installs hosts</tspan><tspan
sodipodi:role="line"
x="275.871"
y="642.71771"
id="tspan3968">(missing) </tspan></text>
</g>
</g>
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 330.53142,605.54234 354.37306,0.15493"
id="path3924"
inkscape:connector-type="polyline"
inkscape:connector-curvature="0"
inkscape:connection-start="#g3791"
inkscape:connection-start-point="d4"
inkscape:connection-end="#g3791-5"
inkscape:connection-end-point="d4" />
<text
xml:space="preserve"
style="font-size:27.59350204px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="447.33084"
y="637.85706"
id="text3926"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3928"
x="447.33084"
y="637.85706">interact</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 244.62052,561.05765 1.06374,-49.13612"
id="path3930"
inkscape:connector-type="polyline"
inkscape:connector-curvature="0"
inkscape:connection-start="#g3791"
inkscape:connection-start-point="d4"
inkscape:connection-end="#g3802"
inkscape:connection-end-point="d4" />
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;display:inline"
d="m 243.08193,649.95108 -1.26428,97.49307"
id="path3932"
inkscape:connector-type="polyline"
inkscape:connector-curvature="0"
inkscape:connection-start="#g3791"
inkscape:connection-start-point="d4"
inkscape:connection-end="#g3802-9"
inkscape:connection-end-point="d4" />
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 771.80236,561.28854 0.0297,-53.22603"
id="path3934"
inkscape:connector-type="polyline"
inkscape:connector-curvature="0"
inkscape:connection-start="#g3791-5"
inkscape:connection-start-point="d4"
inkscape:connection-end="#g3802-3"
inkscape:connection-end-point="d4" />
<g
id="g3802-9-7"
transform="matrix(0.96624748,0,0,1,323.63894,252.66181)">
<g
id="g3900-3"
transform="matrix(1.0748862,0,0,0.96932859,-4.8574514,97.533037)">
<g
id="g3970"
transform="translate(58.55042,-102.35709)">
<rect
y="519.00165"
x="158.40208"
height="144.96896"
width="431.08368"
id="rect3796-1-1"
style="fill:#822a0e;fill-opacity:1;stroke:none" />
<text
sodipodi:linespacing="125%"
id="text3798-3-7"
y="576.61359"
x="374.46384"
style="font-size:40px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
y="576.61359"
x="374.46384"
id="tspan3800-6-3"
sodipodi:role="line">visualises inventory</tspan><tspan
id="tspan3966"
y="626.61359"
x="380.83102"
sodipodi:role="line">(missing) </tspan></text>
</g>
</g>
</g>
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 771.03726,650.18197 -1.54888,92.98943"
id="path3986"
inkscape:connector-type="polyline"
inkscape:connector-curvature="0"
inkscape:connection-start="#g3791-5"
inkscape:connection-start-point="d4"
inkscape:connection-end="#g3802-9-7"
inkscape:connection-end-point="d4" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,44 @@
Old:
- global explores (all)
- initial manifest
- for each object
execute type explorers
execute manifest
continue until all objects (including newly created)
have their type explorers/manifests run
- build dependency tree
- for each object
execute gencode-*
execute code-*
New:
- run all global explorers
- run initial manifest
creates zero or more cdist_objects
- for each cdist_object
if not cdist_object.has_unfullfilled_requirements:
execute type explorers
execute manifest
may create new objects, resulting in autorequirements
# Gained requirements during manifest run
if object.has_auto_requirements():
continue
cdist_object.execute gencode-*
cdist_object.execute code-*
Requirements / Test cases for requirments / resolver:
- omnipotence
-
--------------------------------------------------------------------------------
ERROR: localhost: The following objects could not be resolved: __cdistmarker/singleton requires autorequires ; __directory/etc/sudoers.d requires autorequires ; __file/etc/sudoers.d/nico requires __directory/etc/sudoers.d autorequires ; __file/etc/motd requires autorequires ; __package_pacman/atop requires autorequires ; __package_pacman/screen requires autorequires ; __package_pacman/strace requires autorequires ; __package_pacman/vim requires autorequires ; __package_pacman/zsh requires autorequires ; __package_pacman/lftp requires autorequires ; __package_pacman/nmap requires autorequires ; __package_pacman/ntp requires autorequires ; __package_pacman/rsync requires autorequires ; __package_pacman/rtorrent requires autorequires ; __package_pacman/wget requires autorequires ; __package_pacman/nload requires autorequires ; __package_pacman/iftop requires autorequires ; __package_pacman/mosh requires autorequires ; __package_pacman/git requires autorequires ; __package_pacman/mercurial requires autorequires ; __package_pacman/netcat requires autorequires ; __package_pacman/python-virtualenv requires autorequires ; __package_pacman/wireshark-cli requires autorequires ; __package_pacman/sudo requires autorequires
INFO: Total processing time for 1 host(s): 32.30426597595215
ERROR: Failed to deploy to the following hosts: localhost

Binary file not shown.

View file

@ -62,6 +62,8 @@ def commandline():
parser['configinstall'].add_argument('-i', '--initial-manifest',
help='Path to a cdist manifest or \'-\' to read from stdin.',
dest='manifest', required=False)
parser['configinstall'].add_argument('-n', '--dry-run',
help='Do not execute code', action='store_true')
parser['configinstall'].add_argument('-p', '--parallel',
help='Operate on multiple hosts in parallel',
action='store_true', dest='parallel')
@ -158,7 +160,7 @@ def configinstall(args, mode):
(time_end - time_start))
if len(failed_hosts) > 0:
raise cdist.Error("Failed to deploy to the following hosts: " +
raise cdist.Error("Failed to configure the following hosts: " +
" ".join(failed_hosts))
def configinstall_onehost(host, args, mode, parallel):
@ -177,7 +179,7 @@ def configinstall_onehost(host, args, mode, parallel):
debug=args.debug)
c = mode(context)
c.deploy_and_cleanup()
c.run()
context.cleanup()
except cdist.Error as e: