Quick-n-dirty addition of node environments
Signed-off-by: martin f. krafft <madduck@madduck.net>
diff --git a/doc/source/operations.rst b/doc/source/operations.rst
index 98adf57..42e8a86 100644
--- a/doc/source/operations.rst
+++ b/doc/source/operations.rst
@@ -38,6 +38,8 @@
parameters:
ssh.server:
permit_root_login: no
+environment only relevant for nodes, this allows to specify an "environment"
+ into which the node definition is supposed to be place.
============ ================================================================
Nodes may be defined in subdirectories. However, node names (filename) must be
diff --git a/doc/source/salt.rst b/doc/source/salt.rst
index 25bd53f..f0cc733 100644
--- a/doc/source/salt.rst
+++ b/doc/source/salt.rst
@@ -163,6 +163,7 @@
classes (none) [#nodegroups]_
applications states
parameters pillar
+environment environment
================= ================
.. [#nodegroups] See `Salt issue #5787`_ for steps into the direction of letting
diff --git a/doc/source/todo.rst b/doc/source/todo.rst
index 8f87c26..38df768 100644
--- a/doc/source/todo.rst
+++ b/doc/source/todo.rst
@@ -41,16 +41,6 @@
Ideally, |reclass| could unify the interface so that even templates can be
shared between the various CMS.
-Node environments
------------------
-At least Salt and Puppet support the notion of "environments", but the Salt
-adapter just puts everything into the "base" environment at the moment.
-
-Part of the reason that multiple environments aren't (yet) supported is
-because I don't see the use-case (anymore) with |reclass|. If you still see
-a use-case, then please help me understand it and let's figure out a good way
-to introduce this concept into |reclass|.
-
Membership information
----------------------
It would be nice if |reclass| could provide e.g. the Nagios master node with
diff --git a/reclass/__init__.py b/reclass/__init__.py
index 5c68e12..7cd6c30 100644
--- a/reclass/__init__.py
+++ b/reclass/__init__.py
@@ -11,9 +11,9 @@
from storage.loader import StorageBackendLoader
from storage.memcache_proxy import MemcacheProxy
-def get_storage(storage_type, nodes_uri, classes_uri):
+def get_storage(storage_type, nodes_uri, classes_uri, **kwargs):
storage_class = StorageBackendLoader(storage_type).load()
- return MemcacheProxy(storage_class(nodes_uri, classes_uri))
+ return MemcacheProxy(storage_class(nodes_uri, classes_uri, **kwargs))
def output(data, fmt, pretty_print=False):
diff --git a/reclass/adapters/salt.py b/reclass/adapters/salt.py
index eec2ac3..6483268 100755
--- a/reclass/adapters/salt.py
+++ b/reclass/adapters/salt.py
@@ -27,7 +27,8 @@
nodes_uri, classes_uri = path_mangler(inventory_base_uri,
nodes_uri, classes_uri)
- storage = get_storage(storage_type, nodes_uri, classes_uri)
+ storage = get_storage(storage_type, nodes_uri, classes_uri,
+ default_environment='base')
reclass = Core(storage, class_mappings)
data = reclass.nodeinfo(minion_id)
@@ -35,6 +36,7 @@
params['__reclass__'] = {}
params['__reclass__']['applications'] = data['applications']
params['__reclass__']['classes'] = data['classes']
+ params['__reclass__']['environment'] = data['environment']
return params
@@ -43,11 +45,10 @@
classes_uri=OPT_CLASSES_URI,
class_mappings=None):
- env = 'base'
- # TODO: node environments
nodes_uri, classes_uri = path_mangler(inventory_base_uri,
nodes_uri, classes_uri)
- storage = get_storage(storage_type, nodes_uri, classes_uri)
+ storage = get_storage(storage_type, nodes_uri, classes_uri,
+ default_environment='base')
reclass = Core(storage, class_mappings)
# if the minion_id is not None, then return just the applications for the
@@ -56,15 +57,19 @@
if minion_id is not None:
data = reclass.nodeinfo(minion_id)
applications = data.get('applications', [])
+ env = data['environment']
return {env: applications}
else:
data = reclass.inventory()
nodes = {}
for node_id, node_data in data['nodes'].iteritems():
- nodes[node_id] = node_data['applications']
+ env = node_data['environment']
+ if env not in nodes:
+ nodes[env] = {}
+ nodes[env][node_id] = node_data['applications']
- return {env: nodes}
+ return nodes
def cli():
diff --git a/reclass/cli.py b/reclass/cli.py
index a07404a..5666e16 100644
--- a/reclass/cli.py
+++ b/reclass/cli.py
@@ -27,7 +27,7 @@
defaults=defaults)
storage = get_storage(options.storage_type, options.nodes_uri,
- options.classes_uri)
+ options.classes_uri, default_environment='base')
class_mappings = defaults.get('class_mappings')
reclass = Core(storage, class_mappings)
diff --git a/reclass/core.py b/reclass/core.py
index 07b2c3a..f65fb82 100644
--- a/reclass/core.py
+++ b/reclass/core.py
@@ -116,6 +116,7 @@
def _nodeinfo_as_dict(self, nodename, entity):
ret = {'__reclass__' : {'node': entity.name, 'name': nodename,
'uri': entity.uri,
+ 'environment': entity.environment,
'timestamp': Core._get_timestamp()
},
}
diff --git a/reclass/datatypes/entity.py b/reclass/datatypes/entity.py
index 684705b..573a28c 100644
--- a/reclass/datatypes/entity.py
+++ b/reclass/datatypes/entity.py
@@ -17,7 +17,7 @@
uri of the Entity that is being merged.
'''
def __init__(self, classes=None, applications=None, parameters=None,
- uri=None, name=None):
+ uri=None, name=None, environment=None):
if classes is None: classes = Classes()
self._set_classes(classes)
if applications is None: applications = Applications()
@@ -26,9 +26,11 @@
self._set_parameters(parameters)
self._uri = uri or ''
self._name = name or ''
+ self._environment = environment or ''
name = property(lambda s: s._name)
uri = property(lambda s: s._uri)
+ environment = property(lambda s: s._environment)
classes = property(lambda s: s._classes)
applications = property(lambda s: s._applications)
parameters = property(lambda s: s._parameters)
@@ -57,6 +59,7 @@
self._parameters.merge(other._parameters)
self._name = other.name
self._uri = other.uri
+ self._environment = other.environment
def interpolate(self):
self._parameters.interpolate()
@@ -83,5 +86,6 @@
def as_dict(self):
return {'classes': self._classes.as_list(),
'applications': self._applications.as_list(),
- 'parameters': self._parameters.as_dict()
+ 'parameters': self._parameters.as_dict(),
+ 'environment': self._environment
}
diff --git a/reclass/datatypes/tests/test_entity.py b/reclass/datatypes/tests/test_entity.py
index 0fd5687..17ec9e8 100644
--- a/reclass/datatypes/tests/test_entity.py
+++ b/reclass/datatypes/tests/test_entity.py
@@ -54,6 +54,11 @@
e = Entity(*self._make_instances(**types), uri=uri)
self.assertEqual(e.uri, uri)
+ def test_constructor_empty_env(self, **types):
+ env = 'not base'
+ e = Entity(*self._make_instances(**types), environment=env)
+ self.assertEqual(e.environment, env)
+
def test_equal_empty(self, **types):
instances = self._make_instances(**types)
self.assertEqual(Entity(*instances), Entity(*instances))
@@ -127,13 +132,22 @@
e1.merge(e2)
self.assertEqual(e1.uri, newuri)
+ def test_merge_newenv(self, **types):
+ instances = self._make_instances(**types)
+ newenv = 'new env'
+ e1 = Entity(*instances, environment='env')
+ e2 = Entity(*instances, environment=newenv)
+ e1.merge(e2)
+ self.assertEqual(e1.environment, newenv)
+
def test_as_dict(self, **types):
instances = self._make_instances(**types)
- entity = Entity(*instances, name='test')
+ entity = Entity(*instances, name='test', environment='test')
comp = {}
comp['classes'] = instances[0].as_list()
comp['applications'] = instances[1].as_list()
comp['parameters'] = instances[2].as_dict()
+ comp['environment'] = 'test'
d = entity.as_dict()
self.assertDictEqual(d, comp)
diff --git a/reclass/storage/yaml_fs/__init__.py b/reclass/storage/yaml_fs/__init__.py
index c163f15..6c917e0 100644
--- a/reclass/storage/yaml_fs/__init__.py
+++ b/reclass/storage/yaml_fs/__init__.py
@@ -23,7 +23,7 @@
class ExternalNodeStorage(NodeStorageBase):
- def __init__(self, nodes_uri, classes_uri):
+ def __init__(self, nodes_uri, classes_uri, default_environment=None):
super(ExternalNodeStorage, self).__init__(STORAGE_NAME)
def _handle_node_duplicates(name, uri1, uri2):
@@ -35,6 +35,8 @@
self._classes_uri = classes_uri
self._classes = self._enumerate_inventory(classes_uri)
+ self._default_environment = default_environment
+
nodes_uri = property(lambda self: self._nodes_uri)
classes_uri = property(lambda self: self._classes_uri)
@@ -62,7 +64,7 @@
name = os.path.splitext(relpath)[0]
except KeyError, e:
raise reclass.errors.NodeNotFound(self.name, name, self.nodes_uri)
- entity = YamlFile(path).get_entity(name)
+ entity = YamlFile(path).get_entity(name, self._default_environment)
return entity
def get_class(self, name, nodename=None):
@@ -71,7 +73,7 @@
path = os.path.join(self.classes_uri, self._classes[name])
except KeyError, e:
raise reclass.errors.ClassNotFound(self.name, name, self.classes_uri)
- entity = YamlFile(path).get_entity()
+ entity = YamlFile(path).get_entity(name)
return entity
def enumerate_nodes(self):
diff --git a/reclass/storage/yaml_fs/yamlfile.py b/reclass/storage/yaml_fs/yamlfile.py
index 2178e87..717a911 100644
--- a/reclass/storage/yaml_fs/yamlfile.py
+++ b/reclass/storage/yaml_fs/yamlfile.py
@@ -31,7 +31,7 @@
self._data = data
fp.close()
- def get_entity(self, name=None):
+ def get_entity(self, name=None, default_environment=None):
classes = self._data.get('classes')
if classes is None:
classes = []
@@ -47,11 +47,13 @@
parameters = {}
parameters = datatypes.Parameters(parameters)
+ env = self._data.get('environment', default_environment)
+
if name is None:
name = self._path
return datatypes.Entity(classes, applications, parameters,
- name=name,
+ name=name, environment=env,
uri='yaml_fs://{0}'.format(self._path))
def __repr__(self):