Merge branch 'master' into type__jail_boolean
This commit is contained in:
		
				commit
				
					
						fd8fdf8b54
					
				
			
		
					 12 changed files with 101 additions and 42 deletions
				
			
		
							
								
								
									
										7
									
								
								build
									
										
									
									
									
								
							
							
						
						
									
										7
									
								
								build
									
										
									
									
									
								
							| 
						 | 
					@ -331,14 +331,17 @@ eof
 | 
				
			||||||
        done
 | 
					        done
 | 
				
			||||||
    ;;
 | 
					    ;;
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
    web)
 | 
					    web-doc)
 | 
				
			||||||
        set -e
 | 
					 | 
				
			||||||
        rsync -av "${basedir}/docs/web/" "${WEBTOPDIR}"
 | 
					        rsync -av "${basedir}/docs/web/" "${WEBTOPDIR}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        cd "${WEBDIR}" && git add "${WEBBASE}"
 | 
					        cd "${WEBDIR}" && git add "${WEBBASE}"
 | 
				
			||||||
        cd "${WEBDIR}" && git commit -m "cdist update" "${WEBBASE}" "${WEBPAGE}"
 | 
					        cd "${WEBDIR}" && git commit -m "cdist update" "${WEBBASE}" "${WEBPAGE}"
 | 
				
			||||||
        cd "${WEBDIR}" && make pub
 | 
					        cd "${WEBDIR}" && make pub
 | 
				
			||||||
 | 
					    ;;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    web)
 | 
				
			||||||
 | 
					        set -e
 | 
				
			||||||
 | 
					        $0 web-doc
 | 
				
			||||||
        # Fix ikiwiki, which does not like symlinks for pseudo security
 | 
					        # Fix ikiwiki, which does not like symlinks for pseudo security
 | 
				
			||||||
        ssh tee.schottelius.org \
 | 
					        ssh tee.schottelius.org \
 | 
				
			||||||
          "cd /home/services/www/nico/www.nico.schottelius.org/www/software/cdist/man &&
 | 
					          "cd /home/services/www/nico/www.nico.schottelius.org/www/software/cdist/man &&
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,7 +3,7 @@
 | 
				
			||||||
# not existing and state != absent
 | 
					# not existing and state != absent
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
state="present"
 | 
					state="present"
 | 
				
			||||||
[ -f "$__object/parameter/state" ] state="$(cat "$__object/parameter/state")"
 | 
					[ -f "$__object/parameter/state" ] && state="$(cat "$__object/parameter/state")"
 | 
				
			||||||
[ "$state" = "absent" ] && exit 0
 | 
					[ "$state" = "absent" ] && exit 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
exists="$(cat "$__object/explorer/exists")"
 | 
					exists="$(cat "$__object/explorer/exists")"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -186,8 +186,6 @@ class CdistObject(object):
 | 
				
			||||||
        return os.path.join(self.path, "explorer")
 | 
					        return os.path.join(self.path, "explorer")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    requirements = fsproperty.FileListProperty(lambda obj: os.path.join(obj.absolute_path, 'require'))
 | 
					    requirements = fsproperty.FileListProperty(lambda obj: os.path.join(obj.absolute_path, 'require'))
 | 
				
			||||||
    before = fsproperty.FileListProperty(lambda obj: os.path.join(obj.absolute_path, 'before'))
 | 
					 | 
				
			||||||
    after = fsproperty.FileListProperty(lambda obj: os.path.join(obj.absolute_path, 'after'))
 | 
					 | 
				
			||||||
    autorequire = fsproperty.FileListProperty(lambda obj: os.path.join(obj.absolute_path, 'autorequire'))
 | 
					    autorequire = fsproperty.FileListProperty(lambda obj: os.path.join(obj.absolute_path, 'autorequire'))
 | 
				
			||||||
    parameters = fsproperty.DirectoryDictProperty(lambda obj: os.path.join(obj.base_path, obj.parameter_path))
 | 
					    parameters = fsproperty.DirectoryDictProperty(lambda obj: os.path.join(obj.base_path, obj.parameter_path))
 | 
				
			||||||
    explorers = fsproperty.DirectoryDictProperty(lambda obj: os.path.join(obj.base_path, obj.explorer_path))
 | 
					    explorers = fsproperty.DirectoryDictProperty(lambda obj: os.path.join(obj.base_path, obj.explorer_path))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,7 +23,6 @@
 | 
				
			||||||
import argparse
 | 
					import argparse
 | 
				
			||||||
import logging
 | 
					import logging
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
import warnings
 | 
					 | 
				
			||||||
import sys
 | 
					import sys
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import cdist
 | 
					import cdist
 | 
				
			||||||
| 
						 | 
					@ -93,12 +92,8 @@ class Emulator(object):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def commandline(self):
 | 
					    def commandline(self):
 | 
				
			||||||
        """Parse command line"""
 | 
					        """Parse command line"""
 | 
				
			||||||
        self.meta_parameters = dict.fromkeys(('after', 'before'))
 | 
					 | 
				
			||||||
        meta_parser = argparse.ArgumentParser(add_help=False)
 | 
					 | 
				
			||||||
        for meta_parameter in self.meta_parameters.keys():
 | 
					 | 
				
			||||||
            meta_parser.add_argument('--%s' % meta_parameter, action='append', required=False)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        parser = argparse.ArgumentParser(add_help=False, parents=[meta_parser], argument_default=argparse.SUPPRESS)
 | 
					        parser = argparse.ArgumentParser(add_help=False, argument_default=argparse.SUPPRESS)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for parameter in self.cdist_type.required_parameters:
 | 
					        for parameter in self.cdist_type.required_parameters:
 | 
				
			||||||
            argument = "--" + parameter
 | 
					            argument = "--" + parameter
 | 
				
			||||||
| 
						 | 
					@ -124,11 +119,6 @@ class Emulator(object):
 | 
				
			||||||
        self.args = parser.parse_args(self.argv[1:])
 | 
					        self.args = parser.parse_args(self.argv[1:])
 | 
				
			||||||
        self.log.debug('Args: %s' % self.args)
 | 
					        self.log.debug('Args: %s' % self.args)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Handle meta parameters
 | 
					 | 
				
			||||||
        for meta_parameter in self.meta_parameters.keys():
 | 
					 | 
				
			||||||
            if meta_parameter in self.args:
 | 
					 | 
				
			||||||
                self.meta_parameters[meta_parameter] = getattr(self.args, meta_parameter)
 | 
					 | 
				
			||||||
                delattr(self.args, meta_parameter)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def setup_object(self):
 | 
					    def setup_object(self):
 | 
				
			||||||
        # Setup object_id - FIXME: unset / do not setup anymore!
 | 
					        # Setup object_id - FIXME: unset / do not setup anymore!
 | 
				
			||||||
| 
						 | 
					@ -185,18 +175,10 @@ class Emulator(object):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def record_requirements(self):
 | 
					    def record_requirements(self):
 | 
				
			||||||
        """record requirements"""
 | 
					        """record requirements"""
 | 
				
			||||||
        for key in ('before', 'after'):
 | 
					 | 
				
			||||||
            if key in self.meta_parameters and self.meta_parameters[key]:
 | 
					 | 
				
			||||||
                for value in self.meta_parameters[key]:
 | 
					 | 
				
			||||||
                    self.log.debug("Recording requirement: %s %s %s", self.cdist_object.name, key, value)
 | 
					 | 
				
			||||||
                    dependency_list = getattr(self.cdist_object, key)
 | 
					 | 
				
			||||||
                    # append to the object.after or object.before lists
 | 
					 | 
				
			||||||
                    dependency_list.append(value)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if "require" in self.env:
 | 
					        if "require" in self.env:
 | 
				
			||||||
            warnings.warn("The 'require' envrionment variable is deprecated. Use the --before and --after meta parameters to define dependencies.", category=PendingDeprecationWarning, stacklevel=2)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            requirements = self.env['require']
 | 
					            requirements = self.env['require']
 | 
				
			||||||
 | 
					            self.log.debug("reqs = " + requirements)
 | 
				
			||||||
            for requirement in requirements.split(" "):
 | 
					            for requirement in requirements.split(" "):
 | 
				
			||||||
                # Ignore empty fields - probably the only field anyway
 | 
					                # Ignore empty fields - probably the only field anyway
 | 
				
			||||||
                if len(requirement) == 0: continue
 | 
					                if len(requirement) == 0: continue
 | 
				
			||||||
| 
						 | 
					@ -205,10 +187,11 @@ class Emulator(object):
 | 
				
			||||||
                cdist_object = self.cdist_object.object_from_name(requirement)
 | 
					                cdist_object = self.cdist_object.object_from_name(requirement)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                self.log.debug("Recording requirement: " + requirement)
 | 
					                self.log.debug("Recording requirement: " + requirement)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                # Save the sanitised version, not the user supplied one
 | 
					                # Save the sanitised version, not the user supplied one
 | 
				
			||||||
                # (__file//bar => __file/bar)
 | 
					                # (__file//bar => __file/bar)
 | 
				
			||||||
                # This ensures pattern matching is done against sanitised list
 | 
					                # This ensures pattern matching is done against sanitised list
 | 
				
			||||||
                self.cdist_object.after.append(cdist_object.name)
 | 
					                self.cdist_object.requirements.append(cdist_object.name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def record_auto_requirements(self):
 | 
					    def record_auto_requirements(self):
 | 
				
			||||||
        """An object shall automatically depend on all objects that it defined in it's type manifest.
 | 
					        """An object shall automatically depend on all objects that it defined in it's type manifest.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					# -*- coding: utf-8 -*-
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# 2011-2012 Steven Armstrong (steven-cdist at armstrong.cc)
 | 
					# 2011 Steven Armstrong (steven-cdist at armstrong.cc)
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This file is part of cdist.
 | 
					# This file is part of cdist.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
| 
						 | 
					@ -77,6 +77,7 @@ class DependencyResolver(object):
 | 
				
			||||||
        lists of all dependencies including the key object itself.
 | 
					        lists of all dependencies including the key object itself.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        if self._dependencies is None:
 | 
					        if self._dependencies is None:
 | 
				
			||||||
 | 
					            log.info("Resolving dependencies...")
 | 
				
			||||||
            self._dependencies = d = {}
 | 
					            self._dependencies = d = {}
 | 
				
			||||||
            self._preprocess_requirements()
 | 
					            self._preprocess_requirements()
 | 
				
			||||||
            for name,cdist_object in self.objects.items():
 | 
					            for name,cdist_object in self.objects.items():
 | 
				
			||||||
| 
						 | 
					@ -109,21 +110,10 @@ class DependencyResolver(object):
 | 
				
			||||||
                    raise RequirementNotFoundError(pattern)
 | 
					                    raise RequirementNotFoundError(pattern)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _preprocess_requirements(self):
 | 
					    def _preprocess_requirements(self):
 | 
				
			||||||
        """Find all before, after and autorequire dependencies and merge them
 | 
					        """Find all autorequire dependencies and merge them to be just requirements
 | 
				
			||||||
        to be just requirements for further processing.
 | 
					        for further processing.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        for cdist_object in self.objects.values():
 | 
					        for cdist_object in self.objects.values():
 | 
				
			||||||
            if cdist_object.after:
 | 
					 | 
				
			||||||
                cdist_object.requirements.extend(cdist_object.after)
 | 
					 | 
				
			||||||
                # 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.after = []
 | 
					 | 
				
			||||||
            if cdist_object.before:
 | 
					 | 
				
			||||||
                for other_object in self.find_requirements_by_name(cdist_object.before):
 | 
					 | 
				
			||||||
                    other_object.requirements.append(cdist_object.name)
 | 
					 | 
				
			||||||
                # 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.before = []
 | 
					 | 
				
			||||||
            if cdist_object.autorequire:
 | 
					            if cdist_object.autorequire:
 | 
				
			||||||
                # The objects (children) that this cdist_object (parent) defined
 | 
					                # The objects (children) that this cdist_object (parent) defined
 | 
				
			||||||
                # in it's type manifest shall inherit all explicit requirements 
 | 
					                # in it's type manifest shall inherit all explicit requirements 
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -87,7 +87,6 @@ class ObjectTestCase(test.CdistTestCase):
 | 
				
			||||||
        self.cdist_object.code_local = ''
 | 
					        self.cdist_object.code_local = ''
 | 
				
			||||||
        self.cdist_object.code_remote = ''
 | 
					        self.cdist_object.code_remote = ''
 | 
				
			||||||
        self.cdist_object.state = ''
 | 
					        self.cdist_object.state = ''
 | 
				
			||||||
        self.cdist_object.requirements = []
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_name(self):
 | 
					    def test_name(self):
 | 
				
			||||||
        self.assertEqual(self.cdist_object.name, '__third/moon')
 | 
					        self.assertEqual(self.cdist_object.name, '__third/moon')
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,6 @@ next:
 | 
				
			||||||
	* New Type: __user_groups (Steven Armstrong)
 | 
						* New Type: __user_groups (Steven Armstrong)
 | 
				
			||||||
	* Type __user: Remove --groups support (now provided by __user_groups)
 | 
						* Type __user: Remove --groups support (now provided by __user_groups)
 | 
				
			||||||
	* Type __apt_ppa: Bugfix: Installeded ppa detection (Steven Armstrong)
 | 
						* Type __apt_ppa: Bugfix: Installeded ppa detection (Steven Armstrong)
 | 
				
			||||||
	* Core: Support for --after and --before parameters
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
2.1.0pre8: 2012-11-15
 | 
					2.1.0pre8: 2012-11-15
 | 
				
			||||||
	* Type cleanup: __apt_ppa, __apt_ppa_update_index, __file, 
 | 
						* Type cleanup: __apt_ppa, __apt_ppa_update_index, __file, 
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										3
									
								
								docs/dev/logs/2012-11-21.idea-shell-testing
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								docs/dev/logs/2012-11-21.idea-shell-testing
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					Use roundup for testing included types?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    http://bmizerany.github.com/roundup/
 | 
				
			||||||
							
								
								
									
										6
									
								
								docs/dev/logs/2012-11-21.roadmap-proposal
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								docs/dev/logs/2012-11-21.roadmap-proposal
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,6 @@
 | 
				
			||||||
 | 
					Target version      proposed date       features
 | 
				
			||||||
 | 
					2.1.                2012-12-01          initial support for before/after requirements
 | 
				
			||||||
 | 
					2.2.                2013-03-01          initial notifications support,
 | 
				
			||||||
 | 
					                                        replace require="" with before/after
 | 
				
			||||||
 | 
					2.3.                2013-06-01          installation support: pre-os and install types
 | 
				
			||||||
 | 
					2.4.                2013-09-01          performance speedup via parallelisation
 | 
				
			||||||
| 
						 | 
					@ -30,6 +30,15 @@ Host *
 | 
				
			||||||
  ControlPersist 10
 | 
					  ControlPersist 10
 | 
				
			||||||
--------------------------------------------------------------------------------
 | 
					--------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SPEEDING UP SHELL EXECUTION
 | 
				
			||||||
 | 
					----------------------------
 | 
				
			||||||
 | 
					On the source host, ensure that /bin/sh is *not* bash: bash is quite slow for
 | 
				
			||||||
 | 
					script execution. Instead, you could use dash after installing it:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					ln -sf /bin/dash /bin/sh
 | 
				
			||||||
 | 
					--------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
MULTI MASTER OR ENVIRONMENT SETUPS
 | 
					MULTI MASTER OR ENVIRONMENT SETUPS
 | 
				
			||||||
----------------------------------
 | 
					----------------------------------
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,6 +11,7 @@ cdist is an alternative to other configuration management systems like
 | 
				
			||||||
[cfengine](http://www.cfengine.org/)
 | 
					[cfengine](http://www.cfengine.org/)
 | 
				
			||||||
and [puppet](http://www.puppetlabs.com/).
 | 
					and [puppet](http://www.puppetlabs.com/).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 * [[Why should I use cdist?|why]]
 | 
				
			||||||
 * [[Documentation|documentation]]
 | 
					 * [[Documentation|documentation]]
 | 
				
			||||||
 * [[Supported Operating Systems|os]]
 | 
					 * [[Supported Operating Systems|os]]
 | 
				
			||||||
 * [[Installation|install]]
 | 
					 * [[Installation|install]]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										68
									
								
								docs/web/cdist/why.mdwn
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								docs/web/cdist/why.mdwn
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,68 @@
 | 
				
			||||||
 | 
					[[!meta title="Why should I use cdist?"]]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[!toc]]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					There are several motivations to use cdist, these
 | 
				
			||||||
 | 
					are probably the most popular ones.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Known language
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Cdist is being configured in
 | 
				
			||||||
 | 
					[shell script](https://en.wikipedia.org/wiki/Shell_script).
 | 
				
			||||||
 | 
					Shell script is used by UNIX system engineers for decades.
 | 
				
			||||||
 | 
					So when cdist is introduced, your staff does not need to learn a new
 | 
				
			||||||
 | 
					[DSL](https://en.wikipedia.org/wiki/Domain-specific_language)
 | 
				
			||||||
 | 
					or programming language.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Powerful language
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Not only is shell scripting widely known by system engineers,
 | 
				
			||||||
 | 
					but it is also a very powerful language. Here are some features
 | 
				
			||||||
 | 
					which make daily work easy:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 * Configuration can react dynamicly on explored values
 | 
				
			||||||
 | 
					 * High level string manipulation (using sed, awk, grep)
 | 
				
			||||||
 | 
					 * Conditional support (**if, case**)
 | 
				
			||||||
 | 
					 * Loop support (**for, while**)
 | 
				
			||||||
 | 
					 * Support for dependencies between cdist types
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## More than shell scripting
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If you compare regular shell scripting with cdist, there is one major
 | 
				
			||||||
 | 
					difference: When using cdist types,
 | 
				
			||||||
 | 
					the results are 
 | 
				
			||||||
 | 
					[idempotent](https://en.wikipedia.org/wiki/Idempotence). 
 | 
				
			||||||
 | 
					In practise that means it does not matter in which order you 
 | 
				
			||||||
 | 
					call cdist types, the result is always the same.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Zero dependency configuration management
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Cdist requires very litte on a target system. Even better,
 | 
				
			||||||
 | 
					in almost all cases all dependencies are usually fulfilled.
 | 
				
			||||||
 | 
					Cdist does not require an agent or a high level programming
 | 
				
			||||||
 | 
					languages on the target host: it will run on any host that
 | 
				
			||||||
 | 
					has an **ssh server running** and a posix compatible shell
 | 
				
			||||||
 | 
					(**/bin/sh**).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Push based distribution
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Cdist uses the push based model for configuration. In this
 | 
				
			||||||
 | 
					scenario, one (or more) computers connect the target hosts
 | 
				
			||||||
 | 
					and apply the configuration. That way the source host has
 | 
				
			||||||
 | 
					very little requirements: Cdist can even run on a sysadmin
 | 
				
			||||||
 | 
					notebook that is loosely connected to the network and has
 | 
				
			||||||
 | 
					limited amount of resources.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Furthermore, from a security point of view, only one machine 
 | 
				
			||||||
 | 
					needs access to the target hosts. No target hosts will ever
 | 
				
			||||||
 | 
					need to connect back to the source host, which contains the
 | 
				
			||||||
 | 
					full configuration.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Highly scalable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If at some point you manage more hosts than can be handled from
 | 
				
			||||||
 | 
					a single source host, you can simply add more resources: Either
 | 
				
			||||||
 | 
					add more cores to one host or add hosts.
 | 
				
			||||||
 | 
					Cdist will utilise the given resources in parallel.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[!tag cdist unix]]
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue