Merge pull request #60 from slimakcz/nf_backupninja_only

added backupninja support, by default is disable
diff --git a/.gitreview b/.gitreview
new file mode 100644
index 0000000..2924158
--- /dev/null
+++ b/.gitreview
@@ -0,0 +1,4 @@
+[gerrit]
+host=gerrit.mcp.mirantis.net
+port=29418
+project=salt-formulas/salt.git
diff --git a/.kitchen.yml b/.kitchen.yml
index eac1011..20f4fff 100644
--- a/.kitchen.yml
+++ b/.kitchen.yml
@@ -118,6 +118,16 @@
         master_ssh_root.sls: tests/pillar/master_ssh_minion_root.sls
         master_formulas.sls: tests/pillar/master_formulas.sls
 
+  - name: master-extpillar-composite
+    provisioner:
+      pillars-from-files:
+        salt.sls: tests/pillar/master_single_extpillars.sls
+
+  - name: master-extpillar-reclass
+    provisioner:
+      pillars-from-files:
+        salt.sls: tests/pillar/master_single_extreclass.sls
+
   - name: control-default
     provisioner:
       grains:
diff --git a/README.rst b/README.rst
index 90d62ef..d3a64b0 100644
--- a/README.rst
+++ b/README.rst
@@ -28,6 +28,11 @@
 .. literalinclude:: tests/pillar/master_single_reclass.sls
    :language: yaml
 
+Salt master with multiple ext_pillars
+
+.. literalinclude:: tests/pillar/master_single_extpillars.sls
+   :language: yaml
+
 Salt master with API
 
 .. literalinclude:: tests/pillar/master_api.sls
@@ -189,6 +194,7 @@
       master:
         state_output: changes
 
+
 Salt synchronise node pillar and modules after start
 
 .. code-block:: yaml
@@ -276,6 +282,80 @@
 
     salt-call event.send 'salt/key/remove'
 
+
+Encrypted pillars
+-----------------
+
+Note: NACL + below configuration will be available in Salt > 2017.7.
+
+External resources:
+
+- Tutorial to configure salt + reclass ext_pillar and nacl: http://apealive.net/post/2017-09-salt-nacl-ext-pillar/
+- Saltstack documentation: https://docs.saltstack.com/en/latest/ref/modules/all/salt.modules.nacl.html
+
+Configure salt NACL module:
+
+.. code-block:: shell
+
+  pip install --upgrade libnacl===1.5.2
+  salt-call --local nacl.keygen /etc/salt/pki/master/nacl
+
+    local:
+        saved sk_file:/etc/salt/pki/master/nacl  pk_file: /etc/salt/pki/master/nacl.pub
+
+
+.. code-block:: yaml
+
+    salt:
+      master:
+        pillar:
+          reclass: *reclass
+          nacl:
+            index: 99
+        nacl:
+          box_type: sealedbox
+          sk_file: /etc/salt/pki/master/nacl
+          pk_file: /etc/salt/pki/master/nacl.pub
+          #sk: None
+          #pk: None
+
+NACL encrypt secrets:
+
+  salt-call --local nacl.enc 'my_secret_value' pk_file=/etc/salt/pki/master/nacl.pub
+    hXTkJpC1hcKMS7yZVGESutWrkvzusXfETXkacSklIxYjfWDlMJmR37MlmthdIgjXpg4f2AlBKb8tc9Woma7q
+  # or
+  salt-run nacl.enc 'myotherpass'
+    ADDFD0Rav6p6+63sojl7Htfrncp5rrDVyeE4BSPO7ipq8fZuLDIVAzQLf4PCbDqi+Fau5KD3/J/E+Pw=
+
+
+NACL encrypted values on pillar:
+
+Use Boxed syntax `NACL[CryptedValue=]` to encode value on pillar:
+
+.. code-block:: yaml
+
+  my_pillar:
+    my_nacl:
+        key0: unencrypted_value
+        key1: NACL[hXTkJpC1hcKMS7yZVGESutWrkvzusXfETXkacSklIxYjfWDlMJmR37MlmthdIgjXpg4f2AlBKb8tc9Woma7q]
+
+NACL large files:
+
+.. code-block:: shell
+  salt-call nacl.enc_file /tmp/cert.crt out=/srv/salt/env/dev/cert.nacl
+  # or more advanced
+  cert=$(cat /tmp/cert.crt)
+  salt-call --out=newline_values_only nacl.enc_pub data="$cert" > /srv/salt/env/dev/cert.nacl
+
+
+NACL within template/native pillars:
+
+  pillarexample:
+      user: root
+      password1: {{salt.nacl.dec('DRB7Q6/X5gGSRCTpZyxS6hlbWj0llUA+uaVyvou3vJ4=')|json}}
+      cert_key: {{salt.nacl.dec_file('/srv/salt/env/dev/certs/example.com/cert.nacl')|json}}
+      cert_key2: {{salt.nacl.dec_file('salt:///certs/example.com/cert2.nacl')|json}}
+
 Salt syndic
 -----------
 
diff --git a/_modules/virtng.py b/_modules/virtng.py
index 2bf3766..0a87e56 100644
--- a/_modules/virtng.py
+++ b/_modules/virtng.py
@@ -375,6 +375,24 @@
                   format: qcow2
                   model: virtio
 
+    Example profile for KVM/QEMU with two disks, first is created
+    from specified image, the second is empty:
+
+    .. code-block:: yaml
+
+        virt:
+          disk:
+            two_disks:
+              - system:
+                  size: 8192
+                  format: qcow2
+                  model: virtio
+                  image: http://path/to/image.qcow2
+              - lvm:
+                  size: 32768
+                  format: qcow2
+                  model: virtio
+
     The ``format`` and ``model`` parameters are optional, and will
     default to whatever is best suitable for the active hypervisor.
     '''
@@ -538,96 +556,110 @@
         salt 'hypervisor' virt.init vm_name 4 512 salt://path/to/image.raw
         salt 'hypervisor' virt.init vm_name 4 512 nic=profile disk=profile
     '''
+
     hypervisor = __salt__['config.get']('libvirt:hypervisor', hypervisor)
 
     nicp = _nic_profile(nic, hypervisor, **kwargs)
 
-    diskp = None
-    seedable = False
-    if image:  # with disk template image
-        # if image was used, assume only one disk, i.e. the
-        # 'default' disk profile
-        # TODO: make it possible to use disk profiles and use the
-        # template image as the system disk
-        #diskp = _disk_profile('default', hypervisor, **kwargs)
-	#new diskp TCP cloud
-	diskp = _disk_profile(disk, hypervisor, **kwargs)
-        # When using a disk profile extract the sole dict key of the first
-        # array element as the filename for disk
+    diskp = _disk_profile(disk, hypervisor, **kwargs)
+
+    if image:
+        # Backward compatibility: if 'image' is specified in the VMs arguments
+        # instead of a disk arguments. In this case, 'image' will be assigned
+        # to the first disk for the VM.
         disk_name = next(diskp[0].iterkeys())
-        disk_type = diskp[0][disk_name]['format']
-        disk_file_name = '{0}.{1}'.format(disk_name, disk_type)
-	# disk size TCP cloud
-	disk_size = diskp[0][disk_name]['size']
+        if not diskp[0][disk_name].get('image', None):
+            diskp[0][disk_name]['image'] = image
 
+    # Create multiple disks, empty or from specified images.
+    for disk in diskp:
+        log.debug("Creating disk for VM [ {0} ]: {1}".format(name, disk))
 
-        if hypervisor in ['esxi', 'vmware']:
-            # TODO: we should be copying the image file onto the ESX host
-            raise SaltInvocationError('virt.init does not support image '
-                                      'template template in conjunction '
-                                      'with esxi hypervisor')
-        elif hypervisor in ['qemu', 'kvm']:
-            img_dir = __salt__['config.option']('virt.images')
-            img_dest = os.path.join(
-                img_dir,
-                name,
-                disk_file_name
-            )
-            img_dir = os.path.dirname(img_dest)
-            sfn = __salt__['cp.cache_file'](image, saltenv)
-            if not os.path.isdir(img_dir):
-                os.makedirs(img_dir)
-            try:
-                salt.utils.files.copyfile(sfn, img_dest)
-                mask = os.umask(0)
-                os.umask(mask)
-                # Apply umask and remove exec bit
+        for disk_name, args in disk.items():
 
-		# Resizing image TCP cloud
-		cmd = 'qemu-img resize ' + img_dest  + ' ' +  str(disk_size) + 'M'
- 	        subprocess.call(cmd, shell=True)
-		
-                mode = (0o0777 ^ mask) & 0o0666
-                os.chmod(img_dest, mode)
-
-            except (IOError, OSError) as e:
-                raise CommandExecutionError('problem copying image. {0} - {1}'.format(image, e))
-
-            seedable = True
-        else:
-            log.error('unsupported hypervisor when handling disk image')
-
-    else:
-        # no disk template image specified, create disks based on disk profile
-        diskp = _disk_profile(disk, hypervisor, **kwargs)
-        if hypervisor in ['qemu', 'kvm']:
-            # TODO: we should be creating disks in the local filesystem with
-            # qemu-img
-            raise SaltInvocationError('virt.init does not support disk '
-                                      'profiles in conjunction with '
-                                      'qemu/kvm at this time, use image '
-                                      'template instead')
-        else:
-            # assume libvirt manages disks for us
-            for disk in diskp:
-                for disk_name, args in disk.items():
+            if hypervisor in ['esxi', 'vmware']:
+                if 'image' in args:
+                    # TODO: we should be copying the image file onto the ESX host
+                    raise SaltInvocationError('virt.init does not support image '
+                                              'template template in conjunction '
+                                              'with esxi hypervisor')
+                else:
+                    # assume libvirt manages disks for us
                     xml = _gen_vol_xml(name,
                                        disk_name,
                                        args['size'],
                                        hypervisor)
                     define_vol_xml_str(xml)
 
+            elif hypervisor in ['qemu', 'kvm']:
+
+                disk_type = args['format']
+                disk_file_name = '{0}.{1}'.format(disk_name, disk_type)
+                # disk size TCP cloud
+                disk_size = args['size']
+
+                img_dir = __salt__['config.option']('virt.images')
+                img_dest = os.path.join(
+                    img_dir,
+                    name,
+                    disk_file_name
+                )
+                img_dir = os.path.dirname(img_dest)
+                if not os.path.isdir(img_dir):
+                    os.makedirs(img_dir)
+
+                if 'image' in args:
+                    # Create disk from specified image
+                    sfn = __salt__['cp.cache_file'](args['image'], saltenv)
+                    try:
+                        salt.utils.files.copyfile(sfn, img_dest)
+                        mask = os.umask(0)
+                        os.umask(mask)
+                        # Apply umask and remove exec bit
+
+                        # Resizing image TCP cloud
+                        cmd = 'qemu-img resize ' + img_dest  + ' ' +  str(disk_size) + 'M'
+                        subprocess.call(cmd, shell=True)
+
+                        mode = (0o0777 ^ mask) & 0o0666
+                        os.chmod(img_dest, mode)
+
+                    except (IOError, OSError) as e:
+                        raise CommandExecutionError('problem while copying image. {0} - {1}'.format(args['image'], e))
+
+                    if kwargs.get('seed'):
+                        install = kwargs.get('install', True)
+                        seed_cmd = kwargs.get('seed_cmd', 'seedng.apply')
+
+                        __salt__[seed_cmd](img_dest,
+                                           id_=name,
+                                           config=kwargs.get('config'),
+                                           install=install)
+                else:
+                    # Create empty disk
+                    try:
+                        mask = os.umask(0)
+                        os.umask(mask)
+                        # Apply umask and remove exec bit
+
+                        # Create empty image
+                        cmd = 'qemu-img create -f ' + disk_type + ' '  + img_dest  + ' ' +  str(disk_size) + 'M'
+                        subprocess.call(cmd, shell=True)
+
+                        mode = (0o0777 ^ mask) & 0o0666
+                        os.chmod(img_dest, mode)
+
+                    except (IOError, OSError) as e:
+                        raise CommandExecutionError('problem while creating volume {0} - {1}'.format(img_dest, e))
+
+            else:
+                # Unknown hypervisor
+                raise SaltInvocationError('Unsupported hypervisor when handling disk image: {0}'
+                                          .format(hypervisor))
+
     xml = _gen_xml(name, cpu, mem, diskp, nicp, hypervisor, **kwargs)
     define_xml_str(xml)
 
-    if kwargs.get('seed') and seedable:
-        install = kwargs.get('install', True)
-        seed_cmd = kwargs.get('seed_cmd', 'seedng.apply')
-
-        __salt__[seed_cmd](img_dest,
-                           id_=name,
-                           config=kwargs.get('config'),
-                           install=install)
     if start:
         create(name)
 
diff --git a/metadata/service/master/nacl/init.yml b/metadata/service/master/nacl/init.yml
new file mode 100644
index 0000000..48610b3
--- /dev/null
+++ b/metadata/service/master/nacl/init.yml
@@ -0,0 +1,6 @@
+salt:
+  master:
+    nacl:
+      box_type: sealedbox
+      sk_file: /etc/salt/pki/master/nacl
+      pk_file: /etc/salt/pki/master/nacl.pub
diff --git a/metadata/service/master/pillar/composite/gpg.yml b/metadata/service/master/pillar/composite/gpg.yml
new file mode 100644
index 0000000..92641e8
--- /dev/null
+++ b/metadata/service/master/pillar/composite/gpg.yml
@@ -0,0 +1,9 @@
+classes:
+- service.master.pillar.composite
+parameters:
+  salt:
+    master:
+      pillar:
+        gpg:
+          index: 99
+
diff --git a/metadata/service/master/pillar/composite/init.yml b/metadata/service/master/pillar/composite/init.yml
new file mode 100644
index 0000000..184469e
--- /dev/null
+++ b/metadata/service/master/pillar/composite/init.yml
@@ -0,0 +1,5 @@
+parameters:
+  salt:
+    master:
+      pillar:
+        engine: composite
diff --git a/metadata/service/master/pillar/composite/nacl.yml b/metadata/service/master/pillar/composite/nacl.yml
new file mode 100644
index 0000000..9aa76e6
--- /dev/null
+++ b/metadata/service/master/pillar/composite/nacl.yml
@@ -0,0 +1,11 @@
+classes:
+- service.master.nacl
+- service.master.pillar.composite
+parameters:
+  salt:
+    master:
+      pillar:
+        nacl:
+          # if order is provided 99 is used to compose "99-nacl" key name which is later used to order entries
+          index: 99
+
diff --git a/metadata/service/master/pillar/composite/reclass.yml b/metadata/service/master/pillar/composite/reclass.yml
new file mode 100644
index 0000000..93c8c0d
--- /dev/null
+++ b/metadata/service/master/pillar/composite/reclass.yml
@@ -0,0 +1,14 @@
+classes:
+- service.master.pillar.composite
+parameters:
+  salt:
+    master:
+      pillar:
+        reclass:
+          index: 1
+          storage_type: yaml_fs
+          inventory_base_uri: /srv/salt/reclass
+          #class_mappings: []
+          propagate_pillar_data_to_reclass: False
+          ignore_class_notfound: False
+          #ignore_class_regexp: []
diff --git a/metadata/service/master/pillar/composite/saltclass.yml b/metadata/service/master/pillar/composite/saltclass.yml
new file mode 100644
index 0000000..6dfab2e
--- /dev/null
+++ b/metadata/service/master/pillar/composite/saltclass.yml
@@ -0,0 +1,8 @@
+classes:
+- service.master.pillar.composite
+parameters:
+  salt:
+    master:
+      pillar:
+        saltclass:
+          path: /srv/salt/saltclass
diff --git a/metadata/service/master/pillar/reclass.yml b/metadata/service/master/pillar/reclass.yml
new file mode 100644
index 0000000..f8ba995
--- /dev/null
+++ b/metadata/service/master/pillar/reclass.yml
@@ -0,0 +1,8 @@
+parameters:
+  salt:
+    master:
+      pillar:
+        engine: reclass
+        reclass:
+          storage_type: yaml_fs
+          inventory_base_uri: /srv/salt/reclass
diff --git a/metadata/service/master/pillar/salt.yml b/metadata/service/master/pillar/salt.yml
new file mode 100644
index 0000000..83416bd
--- /dev/null
+++ b/metadata/service/master/pillar/salt.yml
@@ -0,0 +1,7 @@
+parameters:
+  salt:
+    master:
+      pillar:
+        engine: salt
+        salt:
+          path: /srv/salt/pillar
diff --git a/metadata/service/minion/local.yml b/metadata/service/minion/local.yml
index 961cb1e..d1e178f 100644
--- a/metadata/service/minion/local.yml
+++ b/metadata/service/minion/local.yml
@@ -12,4 +12,3 @@
       local: true
       pillar:
         engine: reclass
-        data_dir: /srv/salt/reclass
diff --git a/metadata/service/support.yml b/metadata/service/support.yml
index 3366bc7..5b6e22f 100644
--- a/metadata/service/support.yml
+++ b/metadata/service/support.yml
@@ -3,6 +3,10 @@
     _orchestrate:
       priority: 20
     _support:
+      telegraf:
+        enabled: true
+      prometheus:
+        enabled: true
       collectd:
         enabled: false
       heka:
diff --git a/releasenotes/config.yaml b/releasenotes/config.yaml
new file mode 100644
index 0000000..fe5bc11
--- /dev/null
+++ b/releasenotes/config.yaml
@@ -0,0 +1,59 @@
+---
+# Usage:
+#
+# reno list
+# reno new slug-title --edit
+# reno report --no-show-source
+
+# Change prelude_section_name to 'summary' from default value prelude
+prelude_section_name: summary
+show_source: False
+sections:
+  # summary/prelude section is always included
+  - [features, New Features]
+  - [issues, Known Issues]
+  - [fixes, Bug Fixes]
+  - [other, Other Notes]
+template: |
+  ---
+  # Author the following sections or remove the section if it is not related.
+  # Use one release note per a feature.
+  #
+  # If you miss a section from the list below, please first submit a review
+  # adding it to releasenotes/config.yaml.
+  #
+  summary: >
+    This section is not mandatory. Use it to highlight the change.
+
+  features:
+    - Use list to record summary of features.
+    - |
+      Provide detailed description with examples.
+      Format with reStructuredText.
+
+      .. code-block:: text
+
+         provide model/formula pillar snippets
+
+  issues:
+    - Use list to record known limitations.
+
+  fixes:
+    - Use list to record summary of fixes.
+      Quick and dirty `git log --oneline`.
+
+  other:
+    - Author additional notes for the release.
+    - Format with reStructuredText.
+    - |
+        Use this section if note is not related to one of the common sections:
+        features, issues, upgrade, deprecations, security, fixes, api, cli
+
+        * list item 1
+        * list item 2
+
+        .. code-block:: yaml
+
+          formula:
+            example:
+              enabled: true
diff --git a/releasenotes/notes/add-releasenotes-9c076c7ee8fbe2a4.yaml b/releasenotes/notes/add-releasenotes-9c076c7ee8fbe2a4.yaml
new file mode 100644
index 0000000..54deb69
--- /dev/null
+++ b/releasenotes/notes/add-releasenotes-9c076c7ee8fbe2a4.yaml
@@ -0,0 +1,18 @@
+---
+summary: >
+  Use "reno", an releasenotes configuration tool to record release notes.
+  Documentation: https://docs.openstack.org/reno/latest
+
+  To list/create/show release notes, run following commands:
+
+    .. code-block:: shell
+
+      reno list
+      reno new releasenote-slug-title --edit
+      # use favored $EDITOR to update the note
+      # git add/commit releasenotes/* as usual
+      reno report --no-show-source
+
+other:
+  - |
+    Added `reno <https://docs.openstack.org/reno/latest>_` configuration to the repository.
diff --git a/releasenotes/notes/allow-multiple-ext-pillars-885d28dc8a18ab99.yaml b/releasenotes/notes/allow-multiple-ext-pillars-885d28dc8a18ab99.yaml
new file mode 100644
index 0000000..44d33d3
--- /dev/null
+++ b/releasenotes/notes/allow-multiple-ext-pillars-885d28dc8a18ab99.yaml
@@ -0,0 +1,20 @@
+---
+features:
+  - |
+    Added option to define multiple ext_pillars.
+    Example usage:
+
+    .. code-block:: text
+
+      salt:
+        master:
+          pillar:
+            engine: composite
+            reclass:
+              storage_type: yaml_fs
+              inventory_base_uri: /srv/salt/reclass_encrypted
+            nacl:
+              index: 99
+        nacl:
+          sk_file: /etc/salt/pki/master/nacl
+          pk_file: /etc/salt/pki/master/nacl.pub
diff --git a/salt/files/master.conf b/salt/files/master.conf
index 329ae0d..10ca746 100644
--- a/salt/files/master.conf
+++ b/salt/files/master.conf
@@ -53,48 +53,128 @@
 {%- endif %}
 
 {%- if master.pillar.engine == 'salt' %}
-
 pillar_roots:
   base:
-  - /srv/salt/pillar
-
+  - {{ master.pillar.get('salt', {}).get('path', '/srv/salt/pillar') }}
 {%- endif %}
 
-{%- if master.pillar.engine == 'reclass' %}
+{%- if master.pillar.engine == 'reclass' or (master.pillar.engine == 'composite' and master.pillar.reclass is defined) %}
 
 reclass: &reclass
-  storage_type: yaml_fs
-  inventory_base_uri: /srv/salt/reclass
+  storage_type: {{ master.pillar.get('reclass', {'storage_type': 'yaml_fs'}).storage_type }}
+  inventory_base_uri: {{ master.pillar.get('reclass', {'inventory_base_uri': '/srv/salt/reclass'}).inventory_base_uri }}
+  {# Additional options, for backward compatibility salt:master:pillar might not be defined #}
+  {%- if master.pillar.reclass is defined %}
+  {%- if master.pillar.reclass.reclass_source_path is defined %}
+  reclass_source_path: {{ master.pillar.reclass.reclass_source_path }}
+  {%- endif %}
+  {%- if master.pillar.reclass.get('class_mappings', [])|length > 0 %}
+  class_mappings:
+  {%- for mapping in master.pillar.reclass.class_mappings %}
+  - {{ mapping.target }} {{ mapping.class }}
+  {%- endfor %}
+  {%- endif %}
+  {%- if master.pillar.reclass.get('propagate_pillar_data_to_reclass', False) == True %}
+  propagate_pillar_data_to_reclass: {{ master.pillar.reclass.propagate_pillar_data_to_reclass }}
+  {%- endif %}
+  {%- if master.pillar.reclass.get('ignore_class_notfound', False) == True %}
+  # Below option is not available in upstream reclass, and require fork https://github.com/salt-formulas/reclass
+  ignore_class_notfound: {{ master.pillar.reclass.ignore_class_notfound }}
+  ignore_class_regexp: {{ master.pillar.reclass.ignore_class_regexp }}
+  {%- endif %}
+  {%- endif %}
+{%- endif %}
+
+{%- if master.pillar.engine == 'saltclass' or (master.pillar.engine == 'composite' and master.pillar.saltclass is defined ) %}
+
+saltclass: &saltclass
+  path: {{ master.pillar.saltclass.get('path', '/srv/salt/saltclass') }}
+{%- endif %}
+
+
+{%- if master.pillar.engine in ['composite', 'reclass'] %}
+{# generate indexed list of ext_engines #}
+{# NONE: Might be rewritten, once proved to work properly, with filters: #}
+{# NONE: select('mapping')|selectattr('_index')|sort(attribute='_index') #}
+{%- set ext_engines = {} %}
+{%- for name,engine in master.pillar.iteritems() %}
+{%-   if not engine is mapping %}{% continue %}{% endif %}
+{%-   do engine.update({'name': engine.get('name', name) }) %}
+{%-   set index = engine.get('index', '1')~'-'~name %}
+{%-   do ext_engines.update({ index: engine }) %}
+{%- endfor %}
+{%- if ext_engines|length > 0 or master.pillar.engine == "reclass" %}
 
 ext_pillar:
+  {%- if master.pillar.engine == 'reclass' %}
+  {#- too keep backward compatibility, in case master.pillar.reclass is not defied at all #}
   - reclass: *reclass
+  {%- endif %}
+  {%- for name, engine in ext_engines|dictsort %}
+  {%- if master.pillar.engine == 'composite' and engine.name == 'reclass' %}
+  - reclass: *reclass
+  {%- endif %}
+  {%- if engine.name == 'saltclass' %}
+  - saltclass: *saltclass
+  {%- endif %}
+  {%- if engine.name == 'nacl' %}
+  - nacl: {}
+  {%- endif %}
+  {%- if engine.name == 'gpg' %}
+  - gpg: {}
+  {%- endif %}
+  {%- endfor %}
+{%- endif %}
+{%- endif %}
+
+{%- if master.pillar.engine == 'reclass'
+    or (master.pillar.engine == 'composite' and
+        (master.pillar.saltclass is defined or
+         master.pillar.reclass is defined ))  %}
 
 master_tops:
+  {%- if master.pillar.engine == 'reclass' or (master.pillar.engine == 'composite' and master.pillar.reclass is defined ) %}
   reclass: *reclass
-
+  {%- endif %}
+  {%- if master.pillar.engine == 'saltclass' or (master.pillar.engine == 'composite' and master.pillar.saltclass is defined ) %}
+  saltclass: *saltclass
+  {%- endif %}
 {%- endif %}
 
 {%- for handler in pillar.salt.minion.get("handlers", []) %}
-
 {%- if handler.engine == "udp"%}
+
 logstash_udp_handler:
   host: {{ handler.host }}
   port: {{ handler.port }}
   version: 1
 {%- endif %}
-
 {%- if handler.engine == "zmq"%}
+
 logstash_zmq_handler:
   address: tcp://{{ handler.host }}:{{ handler.port }}
   version: 1
 {%- endif %}
-
 {%- endfor %}
 
 {%- if master.get('order_masters', False) %}
+
 order_masters: True
 {%- endif %}
 
+{%- if master.nacl is defined %}
+
+nacl.config:
+  box_type: {{ master.nacl.get('box_type', 'sealedbox') }}
+  {%- if master.nacl.sk is defined %}
+  sk: {{ master.nacl.sk }}
+  pk: {{ master.nacl.pk }}
+  {%- else %}
+  sk_file: {{ master.nacl.sk_file }}
+  pk_file: {{ master.nacl.pk_file }}
+  {%- endif %}
+{%- endif %}
+
 {#-
 vim: syntax=jinja
 -#}
diff --git a/salt/map.jinja b/salt/map.jinja
index 5df6d2b..ab40acf 100644
--- a/salt/map.jinja
+++ b/salt/map.jinja
@@ -95,9 +95,17 @@
 Debian:
   pkgs:
   - salt-minion
+  dependency_pkgs:
   - python-m2crypto
   - python-psutil
   - python-yaml
+  - python-msgpack
+  - python-oauth
+  dependency_pkgs_pip:
+  - PyYAML
+  - M2Crypto
+  - psutil
+  - oauth
   cert_pkgs:
   - ca-certificates
 Gentoo:
@@ -109,9 +117,16 @@
 RedHat:
   pkgs:
   - salt-minion
+  dependency_pkgs:
   - m2crypto
   - psutils
   - PyYAML
+  - python-oauth
+  dependency_pkgs_pip:
+  - PyYAML
+  - M2Crypto
+  - psutil
+  - oauth
   cert_pkgs:
   - ca-certificates
 {%- endload %}
diff --git a/salt/master/pillar.sls b/salt/master/pillar.sls
index 3e2e715..806511d 100644
--- a/salt/master/pillar.sls
+++ b/salt/master/pillar.sls
@@ -1,4 +1,4 @@
-{%- from "salt/map.jinja" import master with context %}
+{%- from "salt/map.jinja" import master,storage with context %}
 {%- if master.enabled %}
 
 {%- if master.pillar.engine == 'salt' %}
@@ -38,6 +38,7 @@
 
 /srv/salt/reclass/classes/service:
   file.directory:
+  - makedirs: true
   - require:
     - file: reclass_data_dir
 
@@ -47,6 +48,7 @@
 
 /srv/salt/reclass/classes/service/{{ formula_name }}:
   file.symlink:
+  - makedirs: true
   - target: /srv/salt/env/{{ master.system.environment }}/{{ formula_name }}/metadata/service
   - require:
     - file: /srv/salt/reclass/classes/service
@@ -55,7 +57,7 @@
 
 {%- else %}
 
-{%- for environment_name, environment in master.environment.iteritems() %}
+{%- for environment_name, environment in master.get('environment', {}).iteritems() %}
 
 {%- for formula_name, formula in environment.get('formula', {}).iteritems() %}
 
@@ -63,6 +65,7 @@
 
 /srv/salt/reclass/classes/service/{{ formula_name }}:
   file.symlink:
+  - makedirs: true
   {%- if formula.source == 'pkg' %}
   - target: /usr/share/salt-formulas/reclass/service/{{ formula_name }}
   {%- else %}
diff --git a/salt/master/service.sls b/salt/master/service.sls
index 4c0c69b..0c0ce8c 100644
--- a/salt/master/service.sls
+++ b/salt/master/service.sls
@@ -73,7 +73,13 @@
 salt_master_service:
   service.running:
   - name: {{ master.service }}
-  - enable: true
+  - enable: True
+  {%- if grains['saltversioninfo'][0] >= 2017 and grains['saltversioninfo'][1] >= 7 %}
+  - retry:
+    attempts: 2
+    interval: 5
+    splay: 5
+  {%- endif %}
 
 /srv/salt/env:
   file.directory:
diff --git a/salt/meta/prometheus.yml b/salt/meta/prometheus.yml
new file mode 100644
index 0000000..8575ac9
--- /dev/null
+++ b/salt/meta/prometheus.yml
@@ -0,0 +1,32 @@
+{%- if pillar.salt is defined %}
+{%- if pillar.salt.get('master', {}).get('enabled', False) or pillar.salt.get('minion', {}).get('enabled', False) %}
+server:
+  alert:
+  {%- if pillar.salt.get('master', {}).get('enabled', False)  %}
+    SaltMasterProcessDown:
+      if: >-
+        procstat_running{process_name="salt-master"} == 0
+      {%- raw %}
+      labels:
+        severity: warning
+        service: salt-master
+      annotations:
+        summary: 'Salt-master service is down'
+        description: 'Salt-master service is down on node {{ $labels.host }}'
+      {%- endraw %}
+  {%- endif %}
+  {%- if pillar.salt.get('minion', {}).get('enabled', False)  %}
+    SaltMinionProcessDown:
+      if: >-
+        procstat_running{process_name="salt-minion"} == 0
+      {%- raw %}
+      labels:
+        severity: warning
+        service: salt-minion
+      annotations:
+        summary: 'Salt-minion service is down'
+        description: 'Salt-minion service is down on node {{ $labels.host }}'
+      {%- endraw %}
+  {%- endif %}
+{%- endif %}
+{%- endif %}
diff --git a/salt/meta/telegraf.yml b/salt/meta/telegraf.yml
new file mode 100644
index 0000000..77fec3c
--- /dev/null
+++ b/salt/meta/telegraf.yml
@@ -0,0 +1,16 @@
+{%- if pillar.salt is defined %}
+agent:
+  input:
+  {%- if pillar.salt.get('master', {}).get('enabled', False) or pillar.salt.get('minion', {}).get('enabled', False) %}
+    procstat:
+      process:
+        {%- if pillar.salt.get('master', {}).get('enabled', False)  %}
+        salt-master:
+          pattern: salt-master
+        {%- endif %}
+        {%- if pillar.salt.get('minion', {}).get('enabled', False)  %}
+        salt-minion:
+          pattern: salt-minion
+        {%- endif %}
+  {%- endif %}
+{%- endif %}
diff --git a/salt/minion/grains.sls b/salt/minion/grains.sls
index df5205b..9920082 100644
--- a/salt/minion/grains.sls
+++ b/salt/minion/grains.sls
@@ -40,6 +40,8 @@
 salt_minion_grain_{{ service_name }}_{{ name }}_validity_check:
   cmd.wait:
     - name: python -c "import yaml; stream = file('/etc/salt/grains.d/{{ name }}', 'r'); yaml.load(stream); stream.close()"
+    - require:
+      - pkg: salt_minion_dependency_packages
     - watch:
       - file: salt_minion_grain_{{ service_name }}_{{ name }}
     - watch_in:
diff --git a/salt/minion/service.sls b/salt/minion/service.sls
index 7951b2f..36d8aff 100644
--- a/salt/minion/service.sls
+++ b/salt/minion/service.sls
@@ -10,12 +10,20 @@
   - version: {{ minion.source.version }}
   {%- endif %}
 
+salt_minion_dependency_packages:
+  pkg.installed:
+  - pkgs: {{ minion.dependency_pkgs }}
+
 {%- elif minion.source.get('engine', 'pkg') == 'pip' %}
 
 salt_minion_packages:
   pip.installed:
   - name: salt{% if minion.source.version is defined %}=={{ minion.source.version }}{% endif %}
 
+salt_minion_dependency_packages:
+  pkg.installed:
+  - pkgs: {{ minion.dependency_pkgs_pip }}
+
 {%- endif %}
 
 /etc/salt/minion.d/minion.conf:
@@ -57,6 +65,9 @@
   service.running:
     - name: {{ minion.service }}
     - enable: true
+    - require:
+      - pkg: salt_minion_packages
+      - pkg: salt_minion_dependency_packages
     {%- if grains.get('noservices') %}
     - onlyif: /bin/false
     {%- endif %}
@@ -81,5 +92,8 @@
     - name: 'saltutil.sync_all'
     - onchanges:
       - service: salt_minion_service
+    - require:
+      - pkg: salt_minion_packages
+      - pkg: salt_minion_dependency_packages
 
 {%- endif %}
diff --git a/tests/pillar/control_virt.sls b/tests/pillar/control_virt.sls
index e84c5dd..7587594 100644
--- a/tests/pillar/control_virt.sls
+++ b/tests/pillar/control_virt.sls
@@ -1,3 +1,14 @@
+virt:
+  disk:
+    three_disks:
+      - system:
+          size: 4096
+          image: ubuntu.qcow
+      - repository_snapshot:
+          size: 8192
+          image: snapshot.qcow
+      - cinder-volume:
+          size: 2048
 salt:
   minion:
     enabled: true
@@ -16,6 +27,10 @@
       large:
         cpu: 4
         ram: 8
+      medium_three_disks:
+        cpu: 2
+        ram: 4
+        disk_profile: three_disks
     cluster:
       vpc20_infra:
         domain: neco.virt.domain.com
@@ -32,3 +47,7 @@
             provider: node02.domain.com
             image: bubuntu.qcomw
             size: small
+          ubuntu3:
+            provider: node03.domain.com
+            image: meowbuntu.qcom2
+            size: medium_three_disks
diff --git a/tests/pillar/master_single_extpillars.sls b/tests/pillar/master_single_extpillars.sls
new file mode 100644
index 0000000..03521eb
--- /dev/null
+++ b/tests/pillar/master_single_extpillars.sls
@@ -0,0 +1,83 @@
+git:
+  client:
+    enabled: true
+linux:
+  system:
+    enabled: true
+reclass:
+  storage:
+    enabled: true
+    data_source:
+      engine: git
+      branch: master
+      address: 'https://github.com/salt-formulas/openstack-salt.git'
+salt:
+  master:
+    enabled: true
+    command_timeout: 5
+    worker_threads: 2
+    base_environment: prd
+    #environment:
+    # prd:
+    #   formula:
+    #     python:
+    #       source: git
+    #       address: 'https://github.com/salt-formulas/salt-formula-python.git'
+    #       revision: master
+    pillar:
+      engine: composite
+      reclass:
+        # index: 1 is default value
+        index: 1
+        storage_type: yaml_fs
+        inventory_base_uri: /srv/salt/reclass_encrypted
+        class_mappings:
+          - target: '/^cfg\d+/'
+            class:  system.non-existing.class
+        ignore_class_notfound: True
+        ignore_class_regexp:
+          - 'service.*'
+          - '*.fluentd'
+        propagate_pillar_data_to_reclass: False
+      stack: # not yet implemented
+        # https://docs.saltstack.com/en/latest/ref/pillar/all/salt.pillar.stack.html
+        #option 1
+        #path:
+        #  - /path/to/stack.cfg
+        #option 2
+        pillar:environment:
+          dev: path/to/dev/stasck.cfg
+          prod: path/to/prod/stasck.cfg
+        grains:custom:grain:
+          value:
+            - /path/to/stack1.cfg
+            - /path/to/stack2.cfg
+      saltclass:
+        path: /srv/salt/saltclass
+      nacl:
+        # if order is provided 99 is used to compose "99-nacl" key name which is later used to order entries
+        index: 99
+      gpg: {}
+      vault-1: # not yet implemented
+        name: vault
+        path: secret/salt
+      vault-2: # not yet implemented
+        name: vault
+        path: secret/root
+    vault: # not yet implemented
+      # https://docs.saltstack.com/en/latest/ref/modules/all/salt.modules.vault.html
+      name: myvault
+      url: https://vault.service.domain:8200
+      auth:
+          method: token
+          token: 11111111-2222-3333-4444-555555555555
+      policies:
+          - saltstack/minions
+          - saltstack/minion/{minion}
+    nacl:
+      # https://docs.saltstack.com/en/develop/ref/modules/all/salt.modules.nacl.html
+      box_type: sealedbox
+      sk_file: /etc/salt/pki/master/nacl
+      pk_file: /etc/salt/pki/master/nacl.pub
+      #sk: None
+      #pk: None
diff --git a/tests/pillar/master_single_extreclass.sls b/tests/pillar/master_single_extreclass.sls
new file mode 100644
index 0000000..7162dce
--- /dev/null
+++ b/tests/pillar/master_single_extreclass.sls
@@ -0,0 +1,41 @@
+git:
+  client:
+    enabled: true
+linux:
+  system:
+    enabled: true
+reclass:
+  storage:
+    enabled: true
+    data_source:
+      engine: git
+      branch: master
+      address: 'https://github.com/salt-formulas/openstack-salt.git'
+salt:
+  master:
+    enabled: true
+    command_timeout: 5
+    worker_threads: 2
+    base_environment: prd
+    #environment:
+    # prd:
+    #   formula:
+    #     python:
+    #       source: git
+    #       address: 'https://github.com/salt-formulas/salt-formula-python.git'
+    #       revision: master
+    pillar:
+      engine: reclass
+      reclass:
+        # index: 1 is default value
+        index: 1
+        storage_type: yaml_fs
+        inventory_base_uri: /srv/salt/reclass_encrypted
+        class_mappings:
+          - target: '/^cfg\d+/'
+            class:  system.non-existing.class
+        ignore_class_notfound: True
+        ignore_class_regexp:
+          - 'service.*'
+          - '*.fluentd'
+        propagate_pillar_data_to_reclass: False
diff --git a/tests/pillar/master_single_reclass.sls b/tests/pillar/master_single_reclass.sls
index d6db522..19ebfa7 100644
--- a/tests/pillar/master_single_reclass.sls
+++ b/tests/pillar/master_single_reclass.sls
@@ -29,4 +29,8 @@
             name: salt-formula-service02
     pillar:
       engine: reclass
-      data_dir: /srv/salt/reclass
+      reclass:
+        storage_type: yaml_fs
+        inventory_base_uri: /srv/salt/reclass
+        propagate_pillar_data_to_reclass: False
+        reclass_source_path: /tmp/reclass