diff --git a/build.sh b/build.sh
index 021fb480..6a91ff3d 100755
--- a/build.sh
+++ b/build.sh
@@ -127,11 +127,18 @@ case "$1" in
;;
test)
- python3 -m unittest discover test 'test_*.py'
+ PYTHONPATH=$PYTHONPATH:$(pwd -P)/lib \
+ python3 -m cdist.test
+ ;;
+
+ test-install)
+ PYTHONPATH=$PYTHONPATH:$(pwd -P)/lib \
+ python3 -m unittest cdist.test.test_install
;;
test-all)
- python3 -m unittest discover test '*.py'
+ PYTHONPATH=$PYTHONPATH:$(pwd -P)/lib \
+ python3 -m unittest discover lib/cdist/test '*.py'
;;
*)
diff --git a/conf/type/__mkfs/gencode-remote b/conf/type/__mkfs/gencode-remote
new file mode 100755
index 00000000..b3561bad
--- /dev/null
+++ b/conf/type/__mkfs/gencode-remote
@@ -0,0 +1,38 @@
+#!/bin/sh
+#
+# 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 .
+#
+
+device="$(cat "$__object/parameter/device")"
+type="$(cat "$__object/parameter/type")"
+
+if [ "$type" = "swap" ]; then
+ echo "mkswap $device"
+else
+ command="mkfs -t $type -q"
+ if [ -f "$__object/parameter/options" ]; then
+ options="$(cat "$__object/parameter/options")"
+ command="$command $options"
+ fi
+ command="$command $device"
+ if [ -f "$__object/parameter/blocks" ]; then
+ blocks="$(cat "$__object/parameter/blocks")"
+ command="$command $blocks"
+ fi
+ echo "$command"
+fi
diff --git a/conf/type/__mkfs/install b/conf/type/__mkfs/install
new file mode 100644
index 00000000..e69de29b
diff --git a/conf/type/__mkfs/man.text b/conf/type/__mkfs/man.text
new file mode 100644
index 00000000..4320c639
--- /dev/null
+++ b/conf/type/__mkfs/man.text
@@ -0,0 +1,57 @@
+cdist-type__mkfs(7)
+===================
+Steven Armstrong
+
+
+NAME
+----
+cdist-type__mkfs - build a linux file system
+
+
+DESCRIPTION
+-----------
+This cdist type is a wrapper for the mkfs command.
+
+
+REQUIRED PARAMETERS
+-------------------
+type::
+ The filesystem type to use. Same as mkfs -t.
+
+
+OPTIONAL PARAMETERS
+-------------------
+device::
+ defaults to object_id
+
+options::
+ file system-specific options to be passed to the mkfs command
+
+blocks::
+ the number of blocks to be used for the file system
+
+
+EXAMPLES
+--------
+
+--------------------------------------------------------------------------------
+# reiserfs /dev/sda5
+__mkfs /dev/sda5 --type reiserfs
+# same thing with explicit device
+__mkfs whatever --device /dev/sda5 --type reiserfs
+
+# jfs with journal on /dev/sda2
+__mkfs /dev/sda1 --type jfs --options "-j /dev/sda2"
+--------------------------------------------------------------------------------
+
+
+SEE ALSO
+--------
+- cdist-type(7)
+- mkfs(8)
+
+
+COPYING
+-------
+Copyright \(C) 2011 Steven Armstrong. Free use of this software is
+granted under the terms of the GNU General Public License version 3 (GPLv3).
diff --git a/conf/type/__mkfs/manifest b/conf/type/__mkfs/manifest
new file mode 100755
index 00000000..e9d275a4
--- /dev/null
+++ b/conf/type/__mkfs/manifest
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+# 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 .
+#
+
+# set defaults
+if [ -f "$__object/parameter/device" ]; then
+ device="(cat "$__object/parameter/device")"
+else
+ device="/$__object_id"
+ echo "$device" > "$__object/parameter/device"
+fi
+
+type="(cat "$__object/parameter/type")"
+
+options="(cat "$__object/parameter/options")"
diff --git a/conf/type/__mkfs/parameter/optional b/conf/type/__mkfs/parameter/optional
new file mode 100644
index 00000000..86aeae30
--- /dev/null
+++ b/conf/type/__mkfs/parameter/optional
@@ -0,0 +1,3 @@
+device
+options
+blocks
diff --git a/conf/type/__mkfs/parameter/required b/conf/type/__mkfs/parameter/required
new file mode 100644
index 00000000..aa80e646
--- /dev/null
+++ b/conf/type/__mkfs/parameter/required
@@ -0,0 +1 @@
+type
diff --git a/conf/type/__partition_msdos/install b/conf/type/__partition_msdos/install
new file mode 100644
index 00000000..e69de29b
diff --git a/conf/type/__partition_msdos_apply/files/lib.sh b/conf/type/__partition_msdos_apply/files/lib.sh
index f0859aab..5767ea43 100644
--- a/conf/type/__partition_msdos_apply/files/lib.sh
+++ b/conf/type/__partition_msdos_apply/files/lib.sh
@@ -3,7 +3,8 @@ die() {
exit 1
}
debug() {
- echo "[__partition_msdos_apply] $@" >&2
+ #echo "[__partition_msdos_apply] $@" >&2
+ :
}
fdisk_command() {
@@ -51,7 +52,7 @@ create_partition() {
first_minor="${minor}\n"
type_minor="${minor}\n"
primary_extended="l\n"
- [ "$primary_count" > "3" ] && primary_extended=""
+ [ "$primary_count" -gt "3" ] && primary_extended=""
fi
[ -n "${size}" ] && size="+${size}M"
fdisk_command ${device} "n\n${primary_extended}${first_minor}\n${size}\nt\n${type_minor}${type}\n"
diff --git a/conf/type/__partition_msdos_apply/install b/conf/type/__partition_msdos_apply/install
new file mode 100644
index 00000000..e69de29b
diff --git a/doc/dev/logs/2011-10-04 b/doc/dev/logs/2011-10-04
new file mode 100644
index 00000000..f3bb852d
--- /dev/null
+++ b/doc/dev/logs/2011-10-04
@@ -0,0 +1,3 @@
+Testing for single tests:
+ PYTHONPATH=$PYTHONPATH:$(pwd -P)/lib python3 -m unittest cdist.test.test_install.Install.test_explorer_ran
+
diff --git a/doc/dev/logs/2011-10-05 b/doc/dev/logs/2011-10-05
new file mode 100644
index 00000000..39fc48a2
--- /dev/null
+++ b/doc/dev/logs/2011-10-05
@@ -0,0 +1,116 @@
+Config/Install/Deploy/Run:
+ target host
+ remote_cmd_prefix - ssh user@bla sudo foo?????
+ remote_cp_prefix - cp statt scp oder so
+
+ debug -> env für alles += __debug
+
+
+Storage/Metaobject/Tree? == Path?
+ base_dir?
+ nimmt objekte
+
+ Sammelt Objekte
+
+ Ist prepared hier?
+
+Object
+ "Infos" / Datenhalde
+
+ Base_Dir-Abhängigkeit? - wo
+
+ out_dir - wo speichern
+
+ nur eigenes verzeichnis interessant?
+ -> nicht für shell code / aka gencode!
+ -> __global abhängigkeit
+
+ object.gencode()?
+
+ hast du type-explorer?
+ ja?
+ führe JEDEN remote aus
+ speichere ausgabe in object
+ nein:
+ fertig
+ hast du gencode-{local,remote}?
+ ja?
+ führe local oder remote aus
+ speichere ausgabe in s/^gen//
+ nein:
+ fertig
+
+ hast du code-{local,remote}?
+ ja?
+ führe local oder remote aus
+ nein:
+ fertig
+
+ ich habe ...
+ object_id
+ type
+ type.singleton() == False -> require object_id
+ parameter gegeben
+ requirements / order
+
+ type_explorer := methode zum ausführen?
+
+ cdist.object.Object(type, id)
+
+ methoden:
+ gen_code
+ code
+ run_manifest
+ manifest == ort
+
+Type
+ singleton: ja / nein
+ install: ja / nein
+ type_explorer := liste
+
+ optional_parameter
+ required_parameter
+
+ TypeExplorer
+ verwandt oder == explorer
+ Verwandschaft klären!
+
+ sehr abhängig von base_dir!
+ - welche gibt es?
+ - was für optionen haben sie
+
+ cdist.type.Type("/path/to/type")
+ Tree/Path vieh, das liste von $_ speichert
+ Einfach iterieren
+
+
+
+Explorer
+ execute(env)
+ env == __explorer -> nur im explorer
+
+z.B. BaseExplorer oder andersherum GlobalExplorer
+
+Manifest
+
+Exec
+ wrapper um auszuführen,
+ error handling,
+ output redirection (variable, file, beides, socat :-)
+
+
+--------------------------------------------------------------------------------
+
+- base_dir (conf/, type, ...)
+- manifest (initiale)
+ $methode_mit_inhalt_von_manifest?
+ run_manifest(code)
+ ob sinnvoll?
+ geht auch mit stdin oder datei
+
+ stdin -> muss in tmp-datei, für sh -e?
+-
+--------------------------------------------------------------------------------
+
+save output of shell in buffer instead of displaying?
+ -> freedom to decide whether to display or not!
diff --git a/lib/cdist/__init__.py b/lib/cdist/__init__.py
index a0ca2ba2..864b4f37 100644
--- a/lib/cdist/__init__.py
+++ b/lib/cdist/__init__.py
@@ -24,3 +24,13 @@ VERSION = "2.0.3"
class Error(Exception):
"""Base exception class for this project"""
pass
+
+
+class MissingEnvironmentVariableError(Error):
+ """Raised when a required environment variable is not set."""
+
+ def __init__(self, name)
+ self.name = name
+
+ def __str__(self):
+ return 'Missing required environment variable: {0.name}'.format(o)
diff --git a/lib/cdist/config.py b/lib/cdist/config.py
index c027da17..657714a4 100644
--- a/lib/cdist/config.py
+++ b/lib/cdist/config.py
@@ -22,10 +22,6 @@
import datetime
import logging
-import os
-import stat
-import sys
-
log = logging.getLogger(__name__)
import cdist.emulator
@@ -280,10 +276,8 @@ class Config:
self.target_host,
duration.total_seconds())
- def deploy_and_cleanup(self):
- """Do what is most often done: deploy & cleanup"""
- self.deploy_to()
- self.cleanup()
+class Config(cdist.config_install.ConfigInstall):
+ pass
def config(args):
"""Configure remote system"""
diff --git a/lib/cdist/config_install.py b/lib/cdist/config_install.py
new file mode 100644
index 00000000..9a84c2cf
--- /dev/null
+++ b/lib/cdist/config_install.py
@@ -0,0 +1,306 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# 2010-2011 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 .
+#
+#
+
+import datetime
+import logging
+import os
+import stat
+import sys
+
+import cdist.emulator
+import cdist.path
+
+log = logging.getLogger(__name__)
+
+CODE_HEADER = "#!/bin/sh -e\n"
+
+class ConfigInstall:
+ """Class to hold install and config methods"""
+
+ def __init__(self, target_host,
+ initial_manifest=False,
+ remote_user="root",
+ home=None,
+ exec_path=sys.argv[0],
+ debug=False):
+
+ self.target_host = target_host
+ self.debug = debug
+ self.remote_user = remote_user
+ self.exec_path = exec_path
+
+ # FIXME: broken - construct elsewhere!
+ self.remote_prefix = ["ssh", self.remote_user + "@" + self.target_host]
+
+ self.path = cdist.path.Path(self.target_host,
+ initial_manifest=initial_manifest,
+ remote_user=self.remote_user,
+ remote_prefix=self.remote_prefix,
+ base_dir=home,
+ debug=debug)
+
+ self.objects_prepared = []
+
+ def cleanup(self):
+ self.path.cleanup()
+
+ def run_global_explorers(self):
+ """Run global explorers"""
+ log.info("Running global explorers")
+ explorers = self.path.list_global_explorers()
+ if(len(explorers) == 0):
+ raise CdistError("No explorers found in", self.path.global_explorer_dir)
+
+ self.path.transfer_global_explorers()
+ for explorer in explorers:
+ output = self.path.global_explorer_output_path(explorer)
+ output_fd = open(output, mode='w')
+ cmd = []
+ cmd.append("__explorer=" + cdist.path.REMOTE_GLOBAL_EXPLORER_DIR)
+ cmd.append(self.path.remote_global_explorer_path(explorer))
+
+ cdist.exec.run_or_fail(cmd, stdout=output_fd, remote_prefix=self.remote_prefix)
+ output_fd.close()
+
+# FIXME: where to call this from?
+ def run_type_explorer(self, cdist_object):
+ """Run type specific explorers for objects"""
+
+ type = self.path.get_type_from_object(cdist_object)
+ self.path.transfer_type_explorers(type)
+
+ cmd = []
+ cmd.append("__explorer=" + cdist.path.REMOTE_GLOBAL_EXPLORER_DIR)
+ cmd.append("__type_explorer=" + self.path.remote_type_explorer_dir(type))
+ cmd.append("__object=" + self.path.remote_object_dir(cdist_object))
+ cmd.append("__object_id=" + self.path.get_object_id_from_object(cdist_object))
+ cmd.append("__object_fq=" + cdist_object)
+
+ # Need to transfer at least the parameters for objects to be useful
+ self.path.transfer_object_parameter(cdist_object)
+
+ # FIXME: Broken due to refactoring into type.py
+ explorers = self.path.list_type_explorers(type)
+ for explorer in explorers:
+ remote_cmd = cmd + [os.path.join(self.path.remote_type_explorer_dir(type), explorer)]
+ output = os.path.join(self.path.type_explorer_output_dir(cdist_object), explorer)
+ output_fd = open(output, mode='w')
+ log.debug("%s exploring %s using %s storing to %s",
+ cdist_object, explorer, remote_cmd, output)
+
+ cdist.exec.run_or_fail(remote_cmd, stdout=output_fd, remote_prefix=self.remote_prefix)
+ output_fd.close()
+
+ def link_emulator(self):
+ """Link emulator to types"""
+ cdist.emulator.link(self.exec_path,
+ self.path.bin_dir, self.path.list_types())
+
+ def init_deploy(self):
+ """Ensure the base directories are cleaned up"""
+ log.debug("Creating clean directory structure")
+
+ self.path.remove_remote_dir(cdist.path.REMOTE_BASE_DIR)
+ self.path.remote_mkdir(cdist.path.REMOTE_BASE_DIR)
+ self.link_emulator()
+
+ def run_initial_manifest(self):
+ """Run the initial manifest"""
+ log.info("Running initial manifest %s", self.path.initial_manifest)
+ env = { "__manifest" : self.path.manifest_dir }
+ self.run_manifest(self.path.initial_manifest, extra_env=env)
+
+ def run_type_manifest(self, cdist_object):
+ """Run manifest for a specific object"""
+ type = self.path.get_type_from_object(cdist_object)
+ manifest = self.path.type_dir(type, "manifest")
+
+ log.debug("%s: Running %s", cdist_object, manifest)
+ if os.path.exists(manifest):
+ env = { "__object" : self.path.object_dir(cdist_object),
+ "__object_id": self.path.get_object_id_from_object(cdist_object),
+ "__object_fq": cdist_object,
+ "__type": self.path.type_dir(type)
+ }
+ self.run_manifest(manifest, extra_env=env)
+
+ def run_manifest(self, manifest, extra_env=None):
+ """Run a manifest"""
+ log.debug("Running manifest %s, env=%s", manifest, extra_env)
+ env = os.environ.copy()
+ env['PATH'] = self.path.bin_dir + ":" + env['PATH']
+
+ # Information required in every manifest
+ env['__target_host'] = self.target_host
+ env['__global'] = self.path.out_dir
+
+ # Submit debug flag to manifest, can be used by emulator and types
+ if self.debug:
+ env['__debug'] = "yes"
+
+ # Required for recording source
+ env['__cdist_manifest'] = manifest
+
+ # Required to find types
+ env['__cdist_type_base_dir'] = self.path.type_base_dir
+
+ # Other environment stuff
+ if extra_env:
+ env.update(extra_env)
+
+ cdist.exec.shell_run_or_debug_fail(manifest, [manifest], env=env)
+
+ def object_run(self, cdist_object, mode):
+ """Run gencode or code for an object"""
+ log.debug("Running %s from %s", mode, cdist_object)
+ file=os.path.join(self.path.object_dir(cdist_object), "require")
+ requirements = cdist.path.file_to_list(file)
+ type = self.path.get_type_from_object(cdist_object)
+
+ for requirement in requirements:
+ log.debug("Object %s requires %s", cdist_object, requirement)
+ self.object_run(requirement, mode=mode)
+
+ #
+ # Setup env Variable:
+ #
+ env = os.environ.copy()
+ env['__target_host'] = self.target_host
+ env['__global'] = self.path.out_dir
+ env["__object"] = self.path.object_dir(cdist_object)
+ env["__object_id"] = self.path.get_object_id_from_object(cdist_object)
+ env["__object_fq"] = cdist_object
+ env["__type"] = self.path.type_dir(type)
+
+ if mode == "gencode":
+ paths = [
+ self.path.type_dir(type, "gencode-local"),
+ self.path.type_dir(type, "gencode-remote")
+ ]
+ for bin in paths:
+ if os.path.isfile(bin):
+ # omit "gen" from gencode and use it for output base
+ outfile=os.path.join(self.path.object_dir(cdist_object),
+ os.path.basename(bin)[3:])
+
+ outfile_fd = open(outfile, "w")
+
+ # Need to flush to ensure our write is done before stdout write
+ # FIXME: CODE_HEADER needed in our sh -e scenario?
+ outfile_fd.write(CODE_HEADER)
+ outfile_fd.flush()
+
+ cdist.exec.shell_run_or_debug_fail(bin, [bin], env=env, stdout=outfile_fd)
+ outfile_fd.close()
+
+ status = os.stat(outfile)
+
+ # Remove output if empty, else make it executable
+ if status.st_size == len(CODE_HEADER):
+ os.unlink(outfile)
+ else:
+ # Add header and make executable - identically to 0o700
+ os.chmod(outfile, stat.S_IXUSR | stat.S_IRUSR | stat.S_IWUSR)
+
+ # Mark object as changed
+ open(os.path.join(self.path.object_dir(cdist_object), "changed"), "w").close()
+
+
+ if mode == "code":
+ local_dir = self.path.object_dir(cdist_object)
+ remote_dir = self.path.remote_object_dir(cdist_object)
+
+ bin = os.path.join(local_dir, "code-local")
+ if os.path.isfile(bin):
+ cdist.exec.run_or_fail([bin])
+
+
+ local_remote_code = os.path.join(local_dir, "code-remote")
+ remote_remote_code = os.path.join(remote_dir, "code-remote")
+ if os.path.isfile(local_remote_code):
+ self.path.transfer_file(local_remote_code, remote_remote_code)
+ # FIXME: remote_prefix
+ cdist.exec.run_or_fail([remote_remote_code], remote_prefix=self.remote_prefix)
+
+ def stage_prepare(self):
+ """Do everything for a deploy, minus the actual code stage"""
+ self.init_deploy()
+ self.run_global_explorers()
+ self.run_initial_manifest()
+
+ log.info("Running object manifests and type explorers")
+
+ old_objects = []
+ objects = self.path.list_objects()
+
+ # Continue process until no new objects are created anymore
+ while old_objects != objects:
+ old_objects = list(objects)
+ for cdist_object in objects:
+ if cdist_object in self.objects_prepared:
+ log.debug("Skipping rerun of object %s", cdist_object)
+ continue
+ else:
+ # FIXME: run_type_explorer:
+ # object can return type
+ # type has explorers
+ # path knows about where to save explorer output
+ # type = self.path.objects[object].type()
+ # self.path.types['type'].explorers()
+ # for explorer in explorers:
+ # output = cdist.exec.run_debug_or_fail_shell(explorer)
+ # if output:
+ # write_output_to(output, os.path.join(self.path.objects[object].explorer_dir(),explorer) )
+ #
+ self.run_type_explorer(cdist_object)
+ self.run_type_manifest(cdist_object)
+ self.objects_prepared.append(cdist_object)
+
+ objects = self.path.list_objects()
+
+ def stage_run(self):
+ """The final (and real) step of deployment"""
+ log.info("Generating and executing code")
+ # Now do the final steps over the existing objects
+ for cdist_object in self.path.list_objects():
+ log.debug("Run object: %s", cdist_object)
+ self.object_run(cdist_object, mode="gencode")
+ self.object_run(cdist_object, mode="code")
+
+ def deploy_to(self):
+ """Mimic the old deploy to: Deploy to one host"""
+ log.info("Deploying to " + self.target_host)
+ time_start = datetime.datetime.now()
+
+ self.stage_prepare()
+ self.stage_run()
+
+ time_end = datetime.datetime.now()
+ duration = time_end - time_start
+ log.info("Finished run of %s in %s seconds",
+ self.target_host,
+ duration.total_seconds())
+
+ def deploy_and_cleanup(self):
+ """Do what is most often done: deploy & cleanup"""
+ self.deploy_to()
+ self.cleanup()
diff --git a/lib/cdist/core/__init__.py b/lib/cdist/core/__init__.py
new file mode 100644
index 00000000..80310ffc
--- /dev/null
+++ b/lib/cdist/core/__init__.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+#
+# 2010-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 .
+#
+#
+
+__all__ = ['Type', 'Object']
+
+from cdist.core.type import Type
+from cdist.core.object import Object
diff --git a/lib/cdist/core/object.py b/lib/cdist/core/object.py
new file mode 100644
index 00000000..80c2c351
--- /dev/null
+++ b/lib/cdist/core/object.py
@@ -0,0 +1,115 @@
+# -*- coding: utf-8 -*-
+#
+# 2010-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 .
+#
+#
+
+import os
+
+import cdist
+import cdist.path
+
+
+class Object(object):
+ """Represents a cdist object.
+
+ All interaction with objects in cdist should be done through this class.
+ Directly accessing an object through the file system from python code is
+ a bug.
+
+ """
+
+ @staticmethod
+ def base_dir():
+ """Return the absolute path to the top level directory where objects
+ are defined.
+
+ Requires the environment variable '__cdist_out_dir' to be set.
+
+ """
+ try:
+ base_dir = os.path.join(
+ os.environ['__cdist_out_dir'],
+ 'object'
+ )
+ except KeyError as e:
+ raise cdist.MissingEnvironmentVariableError(e.args[0])
+
+ # FIXME: should directory be created elsewhere?
+ if not os.path.isdir(base_dir):
+ os.mkdir(base_dir)
+ return base_dir
+
+ @classmethod
+ def list_objects(cls):
+ """Return a list of object instances"""
+ for object_name in cls.list_object_names():
+ type_name = object_name.split(os.sep)[0]
+ object_id = os.sep.join(object_name.split(os.sep)[1:])
+ yield cls(Type(type_name), object_id=object_id)
+
+ @classmethod
+ def list_type_names(cls):
+ """Return a list of type names"""
+ return os.listdir(cls.base_dir())
+
+ @classmethod
+ def list_object_names(cls):
+ """Return a list of object names"""
+ for path, dirs, files in os.walk(cls.base_dir()):
+ # FIXME: use constant instead of string
+ if cdist.path.DOT_CDIST in dirs:
+ yield os.path.relpath(path, cls.base_dir())
+
+ def __init__(self, type, object_id=None, parameter=None, requirements=None):
+ self.type = type # instance of Type
+ self.object_id = object_id
+ self.qualified_name = os.path.join(self.type.name, self.object_id)
+ self.parameter = parameter or {}
+ self.requirements = requirements or []
+
+ def __repr__(self):
+ return '