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 %}