Merge "Handle file_recv option"
diff --git a/README.rst b/README.rst
index d3a64b0..c4a41f8 100644
--- a/README.rst
+++ b/README.rst
@@ -186,6 +186,19 @@
           ".*":
           - x509.sign_remote_certificate
 
+
+Salt master backup configuration
+
+.. code-block:: yaml
+
+    salt:
+      master:
+        backup: true
+        initial_data:
+          engine: backupninja
+          source: backup-node-host
+          host: original-salt-master-id
+
 Configure verbosity of state output (used for `salt` command)
 
 .. code-block:: yaml
diff --git a/_engines/saltgraph.py b/_engines/saltgraph.py
index 0287449..b70af19 100644
--- a/_engines/saltgraph.py
+++ b/_engines/saltgraph.py
@@ -194,7 +194,7 @@
             opts=__opts__,
             sock_dir=__opts__['sock_dir'],
             listen=True)
-        log.debug('Saltgraph engine started')
+    log.debug('Saltgraph engine started')
 
     while True:
         event = event_bus.get_event()
diff --git a/_modules/modelschema.py b/_modules/modelschema.py
new file mode 100644
index 0000000..cb45153
--- /dev/null
+++ b/_modules/modelschema.py
@@ -0,0 +1,224 @@
+
+from __future__ import absolute_import
+
+import glob
+import json
+import logging
+import os.path
+import yaml
+
+# Import third party libs
+try:
+    from jsonschema import validate as _validate
+    from jsonschema.validators import validator_for as _validator_for
+    from jsonschema.exceptions import SchemaError, ValidationError
+    HAS_JSONSCHEMA = True
+except ImportError:
+    HAS_JSONSCHEMA = False
+
+__virtualname__ = 'modelschema'
+
+LOG = logging.getLogger(__name__)
+
+
+def __virtual__():
+    """
+    Only load if jsonschema library exist.
+    """
+    if not HAS_JSONSCHEMA:
+        return (
+            False,
+            'Can not load module jsonschema: jsonschema library not found')
+    return __virtualname__
+
+
+def _get_base_dir():
+    return __salt__['config.get']('pilllar_schema_path',
+                                  '/usr/share/salt-formulas/env')
+
+
+def _dict_deep_merge(a, b, path=None):
+    """
+    Merges dict(b) into dict(a)
+    """
+    if path is None:
+        path = []
+    for key in b:
+        if key in a:
+            if isinstance(a[key], dict) and isinstance(b[key], dict):
+                _dict_deep_merge(a[key], b[key], path + [str(key)])
+            elif a[key] == b[key]:
+                pass  # same leaf value
+            else:
+                raise Exception(
+                    'Conflict at {}'.format('.'.join(path + [str(key)])))
+        else:
+            a[key] = b[key]
+    return a
+
+
+def schema_list():
+    """
+    Returns list of all defined schema files.
+
+    CLI Examples:
+
+    .. code-block:: bash
+
+        salt-call modelutils.schema_list
+
+
+    """
+    output = {}
+    schemas = glob.glob('{}/*/schemas/*.yaml'.format(_get_base_dir()))
+    for schema in schemas:
+        if os.path.exists(schema):
+            role_name = schema.split('/')[-1].replace('.yaml', '')
+            service_name = schema.split('/')[-3]
+            print role_name, service_name
+            name = '{}-{}'.format(service_name, role_name)
+            output[name] = {
+                'service': service_name,
+                'role': role_name,
+                'path': schema,
+                'valid': schema_validate(service_name, role_name)[name]
+            }
+    return output
+
+
+def schema_get(service, role):
+    """
+    Returns pillar schema for given service and role. If no service and role
+    is specified, method will return all known schemas.
+
+    CLI Examples:
+
+    .. code-block:: bash
+
+        salt-call modelutils.schema_get ntp server
+
+    """
+    schema_path = 'salt://{}/schemas/{}.yaml'.format(service, role)
+    schema = __salt__['cp.get_file_str'](schema_path)
+    if schema:
+        try:
+            data = yaml.safe_load(schema)
+        except yaml.YAMLError as exc:
+            raise Exception("Failed to parse schema:{}\n"
+                            "{}".format(schema_path, exc))
+    else:
+        raise Exception("Schema not found:{}".format(schema_path))
+    return {'{}-{}'.format(service, role): data}
+
+
+def schema_validate(service, role):
+    """
+    Validates pillar schema itself of given service and role.
+
+    CLI Examples:
+
+    .. code-block:: bash
+
+        salt-call modelutils.schema_validate ntp server
+
+    """
+
+    schema = schema_get(service, role)['{}-{}'.format(service, role)]
+    cls = _validator_for(schema)
+    LOG.debug("Validating schema..")
+    try:
+        cls.check_schema(schema)
+        LOG.debug("Schema is valid")
+        data = 'Schema is valid'
+    except SchemaError as exc:
+        LOG.error("SchemaError:{}".format(exc))
+        data = repr(exc)
+    return {'{}-{}'.format(service, role): data}
+
+
+def model_validate(service=None, role=None):
+    """
+    Validates pillar metadata by schema for given service and role. If
+    no service and role is specified, method will validate all defined
+    services.
+
+    CLI Example:
+    .. code-block:: bash
+        salt-run modelschema.model_validate keystone server
+
+    """
+    schema = schema_get(service, role)['{}-{}'.format(service, role)]
+    model = __salt__['pillar.get']('{}:{}'.format(service, role))
+    try:
+        _validate(model, schema)
+        data = 'Model is valid'
+    except SchemaError as exc:
+        LOG.error("SchemaError:{}".format(exc))
+        data = repr(exc)
+    except ValidationError as exc:
+        LOG.error("ValidationError:{}\nInstance:{}\n"
+                  "SchemaPath:{}".format(exc.message, exc.instance,
+                                         exc.schema_path))
+        raise Exception("ValidationError")
+    return {'{}-{}'.format(service, role): data}
+
+
+def data_validate(model, schema):
+    """
+    Validates model by given schema.
+
+    CLI Example:
+    .. code-block:: bash
+        salt-run modelschema.data_validate {'a': 'b'} {'a': 'b'}
+    """
+    try:
+        _validate(model, schema)
+        data = 'Model is valid'
+    except SchemaError as exc:
+        LOG.error("SchemaError:{}".format(exc))
+        data = str(exc)
+    except ValidationError as exc:
+        LOG.error("ValidationError:{}\nInstance:{}\n"
+                  "SchemaPath:{}".format(exc.message, exc.instance,
+                                         exc.schema_path))
+        raise Exception("ValidationError")
+    return data
+
+
+def schema_from_tests(service):
+    """
+    Generate pillar schema skeleton for given service. Method iterates throught
+    test pillars and generates schema scaffold structure in JSON format that
+    can be passed to service like http://jsonschema.net/ to get the basic
+    schema for the individual roles of the service.
+
+    CLI Examples:
+
+    .. code-block:: bash
+
+        salt-call modelutils.schema_from_tests keystone
+    """
+    pillars = glob.glob(
+        '{}/{}/tests/pillar/*.sls'.format(_get_base_dir(), service))
+    raw_data = {}
+    for pillar in pillars:
+        if os.path.exists(pillar):
+            with open(pillar, 'r') as stream:
+                try:
+                    data = yaml.load(stream)
+                except yaml.YAMLError as exc:
+                    data = {}
+                    LOG.error('{}: {}'.format(pillar, repr(exc)))
+            try:
+                _dict_deep_merge(raw_data, data)
+            except Exception as exc:
+                LOG.error('{}: {}'.format(pillar, repr(exc)))
+    if service not in raw_data.keys():
+        raise Exception(
+            "Could not find applicable  data "
+            "for:{}\n at:{}".format(service, _get_base_dir()))
+    data = raw_data[service]
+    output = {}
+    for role_name, role in data.items():
+        output[role_name] = json.dumps(role)
+    return output
diff --git a/_modules/modelutils.py b/_modules/modelutils.py
new file mode 100644
index 0000000..6d0ab40
--- /dev/null
+++ b/_modules/modelutils.py
@@ -0,0 +1,75 @@
+
+from collections import OrderedDict
+
+
+def __virtual__():
+    return True
+
+
+def _set_subtree(node, relationships):
+    return {
+        v: _set_subtree(v, relationships)
+        for v in [x['id'] for x in relationships if node in x['require']]
+    }
+
+
+def _traverse_subtree(output, data):
+    for key, value in data.items():
+        output.append(key)
+        _traverse_subtree(output, value)
+    return output
+
+
+def order_by_requisites(data):
+    '''
+    Returns dictionary ordered by require and require_by
+
+    CLI Examples:
+
+    .. code-block:: bash
+
+        salt-call modelutils.order_by_requisites "{'dict':'value'}""
+
+    Sample data
+
+    passed_data:
+      syslog2:
+        pattern: 'syslog.*'
+      syslog_tele1:
+        type: parser
+        require:
+        - syslog1
+      syslog1:
+        pattern: 'syslog.*'
+        require_in:
+        - syslog2
+      syslog_tele2:
+        require:
+        - syslog_tele1
+
+    '''
+    raw_key_list = []
+    ordered_key_list = []
+    output_dict = OrderedDict()
+
+    for datum_id, datum in data.items():
+        if 'require_in' in datum:
+            for req in datum['require_in']:
+                if 'require' not in data[req]:
+                    data[req]['require'] = []
+                data[req]['require'].append(datum_id)
+            datum.pop('require_in')
+
+    for datum_id, datum in data.items():
+        if 'require' not in datum:
+            datum['require'] = ['top']
+        datum['id'] = datum_id
+        raw_key_list.append(datum)
+
+    tree_data = _set_subtree('top', raw_key_list)
+    _traverse_subtree(ordered_key_list, tree_data)
+    for key in ordered_key_list:
+        output_dict[key] = data[key]
+
+    return output_dict
+
diff --git a/_modules/seedng.py b/_modules/seedng.py
index 7d77a03..1d93c5d 100644
--- a/_modules/seedng.py
+++ b/_modules/seedng.py
@@ -256,8 +256,10 @@
     boot_, tmppath = (prep_bootstrap(mpt)
              or salt.syspaths.BOOTSTRAP)
     # Exec the chroot command
+    arg = 'stable {0}'.format('.'.join(salt.version.__version__.split('.')[:2]))
     cmd = 'if type salt-minion; then exit 0; '
-    cmd += 'else sh {0} -c /tmp; fi'.format(os.path.join(tmppath, 'bootstrap-salt.sh'))
+    cmd += 'else sh {0} -c /tmp {1}; fi'.format(
+        os.path.join(tmppath, 'bootstrap-salt.sh'), arg)
     return not __salt__['cmd.run_chroot'](mpt, cmd, python_shell=True)['retcode']
 
 
diff --git a/_runners/modelschema.py b/_runners/modelschema.py
new file mode 100644
index 0000000..1a82179
--- /dev/null
+++ b/_runners/modelschema.py
@@ -0,0 +1,72 @@
+
+# Import python libs
+import logging
+import glob
+import os
+import yaml
+
+# Import salt modules
+import salt.client
+
+# Import third party libs
+from jsonschema import validate
+from jsonschema.validators import validator_for
+from jsonschema.exceptions import SchemaError
+
+
+__virtualname__ = 'modelschema'
+
+LOG = logging.getLogger(__name__)
+
+BASE_DIR = '/usr/share/salt-formulas/env/_formulas'
+
+
+def _get_schemas():
+    '''
+    Method will return all known schemas.
+    '''
+    output = {}
+    schemas = glob.glob('{}/*/schemas/*.yaml'.format(BASE_DIR))
+    for schema in schemas:
+        if os.path.exists(schema):
+            filename = schema.split('/')[-1].replace('.yaml', '')
+            service_name, role_name = filename.split('-')
+            if service_name not in output:
+                output[service_name] = {}
+            with open(schema, 'r') as stream:
+                try:
+                    data = yaml.load(stream)
+                except yaml.YAMLError as exc:
+                    data = None
+                    LOG.error(exc)
+            output[service_name][role_name] = data
+    return output
+
+
+def validate_node_model(target, service, role):
+    '''
+    Validates pillar by schema for given given minion, service and role.
+    If no service and role is specified, method will validate all
+    defined services on minion.
+
+    CLI Example:
+    .. code-block:: bash
+        salt-run modelschema.validate_node_model
+    '''
+    client = salt.client.LocalClient(__opts__['conf_file'])
+    schema = _get_schemas()[service][role]
+    result = {}
+
+    validator = validator_for(schema)
+    try:
+        validator.check_schema(schema)
+    except SchemaError as exception:
+        LOG.error(exception)
+        return result
+
+    minions = client.cmd(target, 'pillar.data', timeout=1)
+    for minion, pillar in minions.items():
+        model = pillar[service][role]
+        validation_result = validator(schema).validate(model)
+        result[minion] = validation_result
+    return result
diff --git a/salt/files/_engine.conf b/salt/files/_engine.conf
index 7d80849..7edee8e 100644
--- a/salt/files/_engine.conf
+++ b/salt/files/_engine.conf
@@ -1,5 +1,10 @@
 {% from "salt/map.jinja" import master with context %}
 
+engines_dirs:
+{%- for environment_name, environment in master.get('environment', {}).iteritems() %}
+- /srv/salt/env/{{ environment_name }}/_engines
+{%- endfor %}
+
 engines:
 {%- for engine_name, engine in master.engine.items() %}
 {%- set name = engine.get('engine', engine_name) %}
diff --git a/salt/files/minion.conf b/salt/files/minion.conf
index 415dcc2..12509e1 100644
--- a/salt/files/minion.conf
+++ b/salt/files/minion.conf
@@ -92,7 +92,7 @@
   {%- endif %}
 {%- endif %}
 
-{%- if pillar.get('galera', {}).master is defined %}
+{%- if pillar.get('galera', {}).get('master', {}).get('enabled', False) %}
 
 {%- from "galera/map.jinja" import master with context %}
 mysql.unix_socket: {{ master.socket }}
@@ -101,7 +101,7 @@
 mysql.db: 'mysql'
 mysql.charset: 'utf8'
 
-{%- elif pillar.get('galera', {}).slave is defined %}
+{%- elif pillar.get('galera', {}).get('slave', {}).get('enabled', False) %}
 
 {%- from "galera/map.jinja" import slave with context %}
 mysql.unix_socket: {{ slave.socket }}
@@ -110,7 +110,7 @@
 mysql.db: 'mysql'
 mysql.charset: 'utf8'
 
-{%- elif pillar.get('mysql', {}).server is defined %}
+{%- elif pillar.get('mysql', {}).get('server', {}).get('enabled', False) %}
 
 mysql.unix_socket: /var/run/mysqld/mysqld.sock
 {%- if pillar.mysql.server.admin is defined %}
diff --git a/salt/files/restore_master.sh b/salt/files/restore_master.sh
new file mode 100644
index 0000000..ef77ec2
--- /dev/null
+++ b/salt/files/restore_master.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+{%- from "salt/map.jinja" import master with context %}
+
+{%- if master.initial_data is defined %}
+mkdir -p /etc/salt/pki.bak
+mv /etc/salt/pki/* /etc/salt/pki.bak
+scp -r backupninja@{{ master.initial_data.source }}:/srv/backupninja/{{ master.initial_data.host }}/etc/salt/pki/pki.0/* /etc/salt/pki
+{%- if master.pillar.engine == 'reclass' or (master.pillar.engine == 'composite' and master.pillar.reclass is defined) %}
+scp -r backupninja@{{ master.initial_data.source }}:/srv/backupninja/{{ master.initial_data.host }}/srv/salt/reclass/nodes/_generated/_generated.0/* /srv/salt/reclass/nodes/_generated
+{%- endif %}
+{%- endif %}
diff --git a/salt/files/restore_minion.sh b/salt/files/restore_minion.sh
new file mode 100644
index 0000000..f8dc36b
--- /dev/null
+++ b/salt/files/restore_minion.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+{%- from "salt/map.jinja" import minion with context %}
+
+{%- if minion.ca is defined %}
+{%- if minion.initial_data is defined %}
+mkdir -p /etc/pki/pki_ca.bak
+mkdir -p /etc/pki/ca
+mv /etc/pki/ca/* /etc/pki/pki_ca.bak
+scp -r backupninja@{{ minion.initial_data.source }}:/srv/backupninja/{{ minion.initial_data.host }}/etc/pki/ca/ca.0/* /etc/pki/ca
+{%- endif %}
+{%- endif %}
\ No newline at end of file
diff --git a/salt/map.jinja b/salt/map.jinja
index 5bc540c..3a36ada 100644
--- a/salt/map.jinja
+++ b/salt/map.jinja
@@ -179,7 +179,7 @@
   virt_pkgs:
   - libvirt-dev
   - pkg-config
-{% if grains.oscodename == 'trusty' %}
+{% if grains.get('oscodename') == 'trusty' %}
   - libguestfs-tools
 {% endif %}
 
diff --git a/salt/master/env.sls b/salt/master/env.sls
index 4568e20..5d4b8ce 100644
--- a/salt/master/env.sls
+++ b/salt/master/env.sls
@@ -114,6 +114,8 @@
     - /usr/share/salt-formulas/env/_states
     - /usr/share/salt-formulas/env/_grains
     - /usr/share/salt-formulas/env/_formulas
+    - /usr/share/salt-formulas/env/_engines
+    - /usr/share/salt-formulas/env/_runners
   - makedirs: True
 
 salt_env_{{ environment_name }}_dirs:
@@ -132,6 +134,8 @@
     - /srv/salt/env/{{ environment_name }}/_states
     - /srv/salt/env/{{ environment_name }}/_grains
     - /srv/salt/env/{{ environment_name }}/_formulas
+    - /srv/salt/env/{{ environment_name }}/_engines
+    - /srv/salt/env/{{ environment_name }}/_runners
   - makedirs: True
 
 {%- endif %}
@@ -202,6 +206,7 @@
   - branch: {{ formula.branch|default(formula.revision) }}
   {%- endif %}
   {% endif %}
+  - submodules: {{ formula.submodules|default(False) }}
   - force_reset: {{ formula.force_reset|default(False) }}
   - require:
     - file: salt_env_{{ environment_name }}_dirs
@@ -332,7 +337,7 @@
 
 {%- for engine_name, engine in formula.get('engine', {}).iteritems() %}
 
-salt_master_{{ environment_name }}_{{ engine_name }}_state:
+salt_master_{{ environment_name }}_{{ engine_name }}_engine:
   file.symlink:
   - name: /srv/salt/env/{{ environment_name }}/_engines/{{ engine_name }}
   - target: /srv/salt/env/{{ environment_name }}/_formulas/{{ formula_name }}/_engines/{{ engine_name }}
@@ -341,6 +346,17 @@
 
 {%- endfor %}
 
+{%- for runner_name, runner in formula.get('runner', {}).iteritems() %}
+
+salt_master_{{ environment_name }}_{{ runner_name }}_runner:
+  file.symlink:
+  - name: /srv/salt/env/{{ environment_name }}/_engines/{{ runner_name }}
+  - target: /srv/salt/env/{{ environment_name }}/_formulas/{{ formula_name }}/_engines/{{ runner_name }}
+  - force: True
+  - makedirs: True
+
+{%- endfor %}
+
 {%- endif %}
 
 {%- endif %}
diff --git a/salt/master/restore.sls b/salt/master/restore.sls
new file mode 100644
index 0000000..fe0ff3f
--- /dev/null
+++ b/salt/master/restore.sls
@@ -0,0 +1,29 @@
+{%- from "salt/map.jinja" import master with context %}
+{%- if master.enabled %}
+
+{%- if master.initial_data is defined %}
+
+/srv/salt/restore_master.sh:
+  file.managed:
+  - source: salt://salt/files/restore_master.sh
+  - mode: 700
+  - template: jinja
+
+salt_master_restore_state:
+  cmd.run:
+  - name: /srv/salt/restore_master.sh
+  - unless: "test -e /srv/salt/master-restored"
+  - cwd: /root
+  - require:
+    - file: /srv/salt/restore_master.sh
+
+salt_master_restore_completed:
+  file.managed:
+  - name: /srv/salt/master-restored
+  - source: {}
+  - require:
+    - cmd: salt_master_restore_state
+
+{%- endif %}
+
+{%- endif %}
diff --git a/salt/meta/backupninja.yml b/salt/meta/backupninja.yml
index c52ae8c..0a98f44 100644
--- a/salt/meta/backupninja.yml
+++ b/salt/meta/backupninja.yml
@@ -1,5 +1,23 @@
+{%- if pillar.salt is defined %}
+{%- if pillar.salt.get('master', {}).get('enabled', False) or (pillar.salt.get('minion', {}).get('enabled', False) and pillar.salt.get('minion', {}).ca is defined) %}
+  {%- from "salt/map.jinja" import master, minion with context %}
 backup:
   salt:
+    {%- if master.get('backup', False) %}
     fs_includes:
-    - /srv/salt/reclass
+    {%- if master.pillar.engine == 'reclass' or (master.pillar.engine == 'composite' and master.pillar.reclass is defined) %}
+    - /srv/salt/reclass/nodes/_generated
+    {%- endif %}
+    - /etc/salt/pki
+    {%- if minion.get('backup', False) %}
+    - /etc/pki/ca
+    {%- endif %}
+    {%- elif minion.get('backup', False) %}
+    fs_includes:
+    - /etc/pki/ca
+    {%- else %}
+    fs_includes: []
+    {%- endif %}
     fs_excludes: []
+{%- endif %}
+{%- endif %}
diff --git a/salt/meta/salt.yml b/salt/meta/salt.yml
index 6bc9632..cb50afd 100644
--- a/salt/meta/salt.yml
+++ b/salt/meta/salt.yml
@@ -4,7 +4,7 @@
   {%- macro load_grains_file() %}{% include grains_fragment_file ignore missing %}{% endmacro %}
   {%- set grains_yaml = load_grains_file()|load_yaml %}
   {%- if grains_yaml is mapping %}
-    {%- for node in grains_yaml.graph %}
+    {%- for node in grains_yaml.graph if grains_yaml.graph %}
     {%- do service_grains.salt.graph.append(node) %}
     {%- endfor %}
   {%- endif %}
diff --git a/salt/minion/restore.sls b/salt/minion/restore.sls
new file mode 100644
index 0000000..3c42852
--- /dev/null
+++ b/salt/minion/restore.sls
@@ -0,0 +1,33 @@
+{%- from "salt/map.jinja" import minion with context %}
+{%- if minion.enabled %}
+
+{%- if minion.ca is defined %}
+
+{%- if minion.initial_data is defined %}
+
+/srv/salt/restore_minion.sh:
+  file.managed:
+  - source: salt://salt/files/restore_minion.sh
+  - mode: 700
+  - template: jinja
+
+salt_minion_restore_state:
+  cmd.run:
+  - name: /srv/salt/restore_minion.sh
+  - unless: "test -e /srv/salt/minion-restored"
+  - cwd: /root
+  - require:
+    - file: /srv/salt/restore_minion.sh
+
+salt_minion_restore_completed:
+  file.managed:
+  - name: /srv/salt/minion-restored
+  - source: {}
+  - require:
+    - cmd: salt_minion_restore_state
+
+{%- endif %}
+
+{%- endif %}
+
+{%- endif %}
diff --git a/salt/schemas/minion.yaml b/salt/schemas/minion.yaml
new file mode 100644
index 0000000..3a20f77
--- /dev/null
+++ b/salt/schemas/minion.yaml
@@ -0,0 +1,26 @@
+%YAML 1.1
+---
+title: Salt minion role
+description: |
+  Salt service in client role.
+type: object
+additionalProperties: false
+
+properties:
+  enabled:
+    description: |
+      Enables the Salt minion role.
+    type: boolean
+  masters:
+    description: |
+      List of Salt masters to connect to.
+    type: array
+    items:
+      $ref: "#/definitions/master"
+
+definitions:
+  master:
+    description: |
+      Hostname or IP address of the masters server
+    type: string
+    format: hostname-ip
diff --git a/tests/pillar/master_backup.sls b/tests/pillar/master_backup.sls
new file mode 100644
index 0000000..cee3d72
--- /dev/null
+++ b/tests/pillar/master_backup.sls
@@ -0,0 +1,22 @@
+git:
+  client:
+    enabled: true
+linux:
+  system:
+    enabled: true
+salt:
+  master:
+    enabled: true
+    source:
+      engine: pkg
+    pillar:
+      engine: salt
+      source:
+        engine: local
+    environment:
+      prd:
+        formula: {}
+    initial_data:
+      engine: backupninja
+      source: backup-node-host
+      host: original-salt-master-id
\ No newline at end of file
diff --git a/tests/pillar/minion_backup.sls b/tests/pillar/minion_backup.sls
new file mode 100644
index 0000000..419fd0f
--- /dev/null
+++ b/tests/pillar/minion_backup.sls
@@ -0,0 +1,8 @@
+salt:
+  minion:
+    enabled: true
+    backup: true
+    initial_data:
+      engine: backupninja
+      source: backup-node-host
+      host: original-salt-master-id
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index 3f42101..9451611 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -110,7 +110,7 @@
 }
 
 salt_run() {
-    [ -e ${VEN_DIR}/bin/activate ] && source ${VENV_DIR}/bin/activate
+    [ -e ${VENV_DIR}/bin/activate ] && source ${VENV_DIR}/bin/activate
     salt-call ${SALT_OPTS} $*
 }