Multiple node generation
diff --git a/README.rst b/README.rst
index a412413..82fca45 100644
--- a/README.rst
+++ b/README.rst
@@ -32,6 +32,54 @@
         data_source:
           engine: local
 
+Reclass model with single node definition
+
+.. code-block:: yaml
+
+    reclass:
+      storage:
+        enabled: true
+        node:
+          service_node01:
+            name: svc01
+            domain: deployment.local
+            classes:
+            - cluster.deployment_name.service.role
+            params:
+              salt_master_host: <<salt-master-ip>>
+              linux_system_codename: trusty
+              single_address: <<node-ip>>
+
+Reclass model with multiple node defined
+
+.. code-block:: yaml
+
+    reclass:
+      storage:
+        enabled: true
+        repeat_replace_symbol: '<<count>>'
+        node:
+          service_node01:
+            name: node<<count>>
+            domain: deployment.local
+            classes:
+            - cluster.deployment.service.role
+            repeat:
+              count: 2
+              start: 5
+              digits: 2
+              params:
+                single_address:
+                  value: 10.0.0.<<count>>
+                  start: 100
+                deploy_address:
+                  value: part-<<count>>-whole
+                  start: 5
+                  digits: 3
+            params:
+              salt_master_host: <<salt-master-ip>>
+              linux_system_codename: trusty
+
 Reclass storage with arbitrary class mappings
 
 .. code-block:: yaml
diff --git a/_modules/reclass.py b/_modules/reclass.py
index 5b5d97a..34ea4b9 100644
--- a/_modules/reclass.py
+++ b/_modules/reclass.py
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 '''
-Module for handling reclass files.
+Module for handling reclass metadata models.
 
 '''
 
@@ -13,10 +13,11 @@
 import yaml
 import json
 
-LOG = logging.getLogger(__name__)
+from reclass import get_storage, output
+from reclass.core import Core
+from reclass.config import find_and_read_configfile
 
-RECLASS_NODES_DIR = "/srv/salt/reclass/nodes"
-RECLASS_CLASSES_DIR = "/srv/salt/reclass/classes"
+LOG = logging.getLogger(__name__)
 
 
 def __virtual__():
@@ -27,7 +28,14 @@
     return 'reclass'
 
 
-__opts__ = {}
+def _get_nodes_dir():
+    defaults = find_and_read_configfile()
+    return os.path.join(defaults.get('inventory_base_uri'), 'nodes')
+
+
+def _get_classes_dir():
+    defaults = find_and_read_configfile()
+    return os.path.join(defaults.get('inventory_base_uri'), 'classes')
 
 
 def node_create(name, path=None, cluster="default", environment="prd", classes=None, parameters=None, **kwargs):
@@ -94,15 +102,16 @@
     LOG.debug(node_meta)
 
     if path == None:
-        file_path = os.path.join(RECLASS_NODES_DIR, name + '.yml')
+        file_path = os.path.join(_get_nodes_dir(), name + '.yml')
     else:
-        file_path = os.path.join(RECLASS_NODES_DIR, path, name + '.yml')
+        file_path = os.path.join(_get_nodes_dir(), path, name + '.yml')
 
     with open(file_path, 'w') as node_file:
         node_file.write(yaml.safe_dump(node_meta, default_flow_style=False))
 
     return node_get(name)
 
+
 def node_delete(name, **kwargs):
     '''
     Delete a reclass node
@@ -123,9 +132,9 @@
         return {'Error': 'Unable to retreive node'}
 
     if node[name]['path'] == '':
-        file_path = os.path.join(RECLASS_NODES_DIR, name + '.yml')
+        file_path = os.path.join(_get_nodes_dir(), name + '.yml')
     else:
-        file_path = os.path.join(RECLASS_NODES_DIR, node[name]['path'], name + '.yml')
+        file_path = os.path.join(_get_nodes_dir(), node[name]['path'], name + '.yml')
 
     os.remove(file_path)
 
@@ -166,7 +175,7 @@
     '''
     ret = {}
 
-    for root, sub_folders, files in os.walk(RECLASS_NODES_DIR):
+    for root, sub_folders, files in os.walk(_get_nodes_dir()):
         for file in files:
             file_path = os.path.join(root, file)
             file_content = open(file_path, 'r')
@@ -186,19 +195,20 @@
             name = file.replace('.yml', '')
             host_name = name.split('.')[0]
             domain_name = '.'.join(name.split('.')[1:])
-            path = root.replace(RECLASS_NODES_DIR+'/', '')
+            path = root.replace(_get_nodes_dir()+'/', '')
             ret[name] = {
-              'name': host_name,
-              'domain': domain_name,
-              'cluster': 'default',
-              'environment': 'prd',
-              'path': path,
-              'classes': classes,
-              'parameters': parameters
+                'name': host_name,
+                'domain': domain_name,
+                'cluster': 'default',
+                'environment': 'prd',
+                'path': path,
+                'classes': classes,
+                'parameters': parameters
             }
 
     return ret
 
+
 def node_update(name, classes=None, parameters=None, **connection_args):
     '''
     Update a node metadata information, classes and parameters.
@@ -214,3 +224,35 @@
         node = node[name.split("/")[1]]
     else:
         return {'Error': 'Error in retrieving node'}
+
+
+def inventory(**connection_args):
+    '''
+    Get all nodes in inventory and their associated services/roles classification.
+
+    CLI Examples:
+
+    .. code-block:: bash
+
+        salt '*' reclass.inventory
+    '''
+    defaults = find_and_read_configfile()
+    storage = get_storage(defaults['storage_type'], _get_nodes_dir(), _get_classes_dir())
+    reclass = Core(storage, None)
+    nodes = reclass.inventory()["nodes"]
+    output = {}
+
+    for node in nodes:
+        service_classification = []
+        role_classification = []
+        for service in nodes[node]['parameters']:
+            if service not in ['_param', 'private_keys', 'public_keys', 'known_hosts']:
+                service_classification.append(service)
+                for role in nodes[node]['parameters'][service]:
+                    if role not in ['_support', '_orchestrate', 'common']:
+                        role_classification.append('%s.%s' % (service, role))
+        output[node] = {
+            'roles': role_classification,
+            'services': service_classification,
+        }
+    return output
diff --git a/reclass/files/node.yml b/reclass/files/node.yml
index 2483710..e8e62ad 100644
--- a/reclass/files/node.yml
+++ b/reclass/files/node.yml
@@ -1,18 +1,20 @@
-{%- set node = salt['pillar.get']('reclass:storage:node:'+node_name) %}
 classes:
 {%- for class in node.classes %}
 - {{ class }}
 {%- endfor %}
 parameters:
-  {%- if node.params is defined %}
+  {%- if node.params is defined or extra_params|length > 0 %}
   _param:
-  {%- for param_name, param_value in node.params.iteritems() %}
+    {%- for param_name, param_value in node.params.iteritems() %}
     {{ param_name }}: {{ param_value }}
-  {%- endfor %}
+    {%- endfor %}
+    {%- for param_name, param_value in extra_params.iteritems() %}
+    {{ param_name }}: {{ param_value }}
+    {%- endfor %}
   {%- endif %}
   {{ node.get('kernel', 'linux') }}:
     system:
-      name: {{ node.name }}
+      name: {{ node_name }}
       domain: {{ node.domain }}
       cluster: {{ node.get('cluster', 'default') }}
       environment: {{ node.get('environment', 'prd') }}
diff --git a/reclass/init.sls b/reclass/init.sls
index e3ecb59..2cad2cb 100644
--- a/reclass/init.sls
+++ b/reclass/init.sls
@@ -1,7 +1,6 @@
-
 {%- if pillar.reclass is defined %}
 include:
 {%- if pillar.reclass.storage is defined %}
 - reclass.storage
 {%- endif %}
-{%- endif %}
\ No newline at end of file
+{%- endif %}
diff --git a/reclass/map.jinja b/reclass/map.jinja
index 02fab07..4514c80 100644
--- a/reclass/map.jinja
+++ b/reclass/map.jinja
@@ -6,6 +6,7 @@
             'engine': 'local'
         },
         'storage_type': 'yaml_fs',
+        'repeat_replace_symbol': '<<count>>',
         'version': '1.4.1',
     },
     'RedHat': {
@@ -15,6 +16,7 @@
             'engine': 'local'
         },
         'storage_type': 'yaml_fs',
+        'repeat_replace_symbol': '<<count>>',
         'version': '1.4.1',
     },
 }, grain='os_family', merge=salt['pillar.get']('reclass:storage')) %}
diff --git a/reclass/storage/node.sls b/reclass/storage/node.sls
index 334778a..0e911f6 100644
--- a/reclass/storage/node.sls
+++ b/reclass/storage/node.sls
@@ -6,6 +6,36 @@
 
 {%- for node_name, node in storage.node.iteritems() %}
 
+{%- if node.repeat is defined %}
+
+{%- for i in range(node.repeat.count) %}
+
+{%- set extra_params = {} %}
+
+{%- for param_name, param in node.repeat.params.iteritems() %}
+{%- set param_count = (param.get('start', 1) + i)|string %}
+{%- set param_value = param.value|replace(storage.repeat_replace_symbol, param_count.rjust(param.get('digits', 1), '0')) %}
+{%- do extra_params.update({param_name: param_value}) %}
+{%- endfor %}
+
+{%- set node_count = (node.repeat.get('start', 1) + i)|string %}
+{%- set node_name = node.name|replace(storage.repeat_replace_symbol, node_count.rjust(node.repeat.get('digits', 1), '0')) %}
+
+{{ storage.base_dir }}/nodes/_generated/{{ node_name }}.{{ node.domain }}.yml:
+  file.managed:
+  - source: salt://reclass/files/node.yml
+  - user: root
+  - group: root
+  - template: jinja
+  - defaults:
+      node: {{ node|yaml }}
+      node_name: "{{ node_name }}"
+      extra_params: {{ extra_params }}
+
+{%- endfor %}
+
+{%- else %}
+
 {{ storage.base_dir }}/nodes/_generated/{{ node.name }}.{{ node.domain }}.yml:
   file.managed:
   - source: salt://reclass/files/node.yml
@@ -13,7 +43,11 @@
   - group: root
   - template: jinja
   - defaults:
-      node_name: "{{ node_name }}"
+      node: {{ node|yaml }}
+      node_name: "{{ node.get('name', node_name) }}"
+      extra_params: {}
+
+{%- endif %}
 
 {%- endfor %}