reclass.storage.node: Merge duplicate nodes

Reclass does not support duplicate nodes in top pillar, so merge all
nodes with the same name into a single node, inheriting classes from
all instances.

This allows using multiple "system.reclass.storage.system.*_cluster"
classes for the same node, based on re-using the name (hostname); by
simply overriding the hostname parameters, e.g.:
  openstack_gateway_node01_hostname: ctl01

NOTE: defaults.merge module does not merge lists (e.g. for classes),
so handle that case separately. Also, leave repeated nodes alone.

Signed-off-by: Alexandru Avadanii <Alexandru.Avadanii@enea.com>
diff --git a/reclass/storage/node.sls b/reclass/storage/node.sls
index 3014b4a..bf27610 100644
--- a/reclass/storage/node.sls
+++ b/reclass/storage/node.sls
@@ -13,8 +13,25 @@
     - clean: True
 {%- endif %}
 
+{%- set storage_by_name = {} %}
+
 {%- for node_name, node in storage.get('node', {}).iteritems() %}
 
+{%- if node.name is defined and node.repeat is not defined %}
+{%- set node_name = node.name %}
+
+{%- if node_name in storage_by_name and storage_by_name[node_name].classes is defined %}
+{%- do node.update({'classes': storage_by_name[node_name].classes + node.get('classes', []) }) %}
+{%- endif %}
+
+{%- endif %}
+
+{%- do salt['defaults.merge'](storage_by_name, {node_name: node}) %}
+
+{%- endfor %}
+
+{%- for node_name, node in storage_by_name.iteritems() %}
+
 {%- if node.repeat is defined %}
 
 {%- for i in range(node.repeat.count) %}
diff --git a/tests/pillar/storage_nodes_squash.sls b/tests/pillar/storage_nodes_squash.sls
new file mode 100644
index 0000000..44b1bd8
--- /dev/null
+++ b/tests/pillar/storage_nodes_squash.sls
@@ -0,0 +1,14 @@
+reclass:
+  storage:
+    enabled: true
+    node:
+      service_node01:
+        name: svc01
+        domain: deployment.local
+        classes:
+        - cluster.deployment_name.service.role
+      service_node02:
+        name: svc01
+        domain: deployment.local
+        classes:
+        - cluster.deployment_name.service.role2