Change to using a list of mappings to maintain order
Signed-off-by: martin f. krafft <madduck@madduck.net>
diff --git a/doc/source/operations.rst b/doc/source/operations.rst
index 0bbf599..a157403 100644
--- a/doc/source/operations.rst
+++ b/doc/source/operations.rst
@@ -92,6 +92,11 @@
can be assigned to each mapping by providing a space-separated list (class
names cannot contain spaces anyway).
+Note that mappings are not designed to replace node definitions. Mappings can
+be used to pre-populate the classes of existing nodes, but you still need to
+define all nodes (and if only to allow them to be enumerated for the
+inventory).
+
Parameter interpolation
------------------------
Parameters may reference each other, including deep references, e.g.::
diff --git a/reclass/errors.py b/reclass/errors.py
index ac06098..d3a0239 100644
--- a/reclass/errors.py
+++ b/reclass/errors.py
@@ -129,6 +129,18 @@
super(InfiniteRecursionError, self).__init__(msg)
+class MappingError(ReclassException):
+
+ def __init__(self, msg, rc=posix.EX_DATAERR):
+ super(MappingError, self).__init__(msg, rc)
+
+
+class MappingFormatError(MappingError):
+
+ def __init__(self, msg):
+ super(MappingFormatError, self).__init__(msg)
+
+
class NameError(ReclassException):
def __init__(self, msg, rc=posix.EX_DATAERR):
diff --git a/reclass/storage/__init__.py b/reclass/storage/__init__.py
index d4d4d35..96f3624 100644
--- a/reclass/storage/__init__.py
+++ b/reclass/storage/__init__.py
@@ -12,7 +12,9 @@
import re
import sys
import fnmatch
+import shlex
from reclass.datatypes import Entity, Classes
+from reclass.errors import MappingFormatError
def _get_timestamp():
return time.strftime('%c')
@@ -39,20 +41,37 @@
def _match_glob(self, key, nodename):
return fnmatch.fnmatchcase(nodename, key)
+ def _shlex_split(self, instr):
+ lexer = shlex.shlex(instr, posix=True)
+ lexer.whitespace_split = True
+ lexer.commenters = ''
+ regexp = False
+ if instr[0] == '/':
+ lexer.quotes += '/'
+ regexp = True
+ try:
+ key = lexer.get_token()
+ except ValueError, e:
+ raise MappingFormatError('Error in mapping "{0}": missing closing '
+ 'quote (or slash)'.format(instr))
+ if regexp:
+ key = '/{0}/'.format(key)
+ return key, list(lexer)
+
def _populate_with_class_mappings(self, nodename):
+ if not self.class_mappings:
+ return Entity(name='empty')
c = Classes()
- for key, value in self.class_mappings.iteritems():
- match = False
- if key.startswith('/') and key.endswith('/'):
- match = self._match_regexp(key[1:-1], nodename)
+ for mapping in self.class_mappings:
+ matched = False
+ key, klasses = self._shlex_split(mapping)
+ if key[0] == ('/'):
+ matched = self._match_regexp(key[1:-1], nodename)
else:
- match = self._match_glob(key, nodename)
- if match:
- if isinstance(value, (types.ListType, types.TupleType)):
- for v in value:
- c.append_if_new(v)
- else:
- c.append_if_new(value)
+ matched = self._match_glob(key, nodename)
+ if matched:
+ for klass in klasses:
+ c.append_if_new(klass)
return Entity(classes=c,
name='class mappings for node {0}'.format(nodename))