Initial commit
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
new file mode 100644
index 0000000..6317b51
--- /dev/null
+++ b/CHANGELOG.rst
@@ -0,0 +1,14 @@
+
+============
+salt-formula
+============
+
+0.0.2
+-----
+
+- Graphing of salt minion states
+
+0.0.1
+-----
+
+- Initial state for installing Salt master
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..6f2b42f
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,13 @@
+Copyright (c) 2014-2015 tcp cloud a.s.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
\ No newline at end of file
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..788cf53
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,323 @@
+
+====  
+Salt
+====
+
+Salt is a new approach to infrastructure management. Easy enough to get running in minutes, scalable enough to manage tens of thousands of servers, and fast enough to communicate with them in seconds.
+
+Salt delivers a dynamic communication bus for infrastructures that can be used for orchestration, remote execution, configuration management and much more.
+
+Sample pillars
+==============
+
+Salt master
+-----------
+
+Salt master with base environment and pillar metadata source
+
+.. code-block:: yaml
+
+    salt:
+      master:
+        enabled: true
+        command_timeout: 5
+        worker_threads: 2
+        pillar:
+          engine: salt
+          source:
+            engine: git
+            address: 'git@repo.domain.com:salt/pillar-demo.git'
+            branch: 'master'
+        base_environment: prd
+        environment:
+          prd:
+            enabled: true
+            formula:
+              linux:
+                source: git
+                address: 'git@repo.domain.com:salt/formula-linux.git'
+                branch: 'master'
+              salt:
+                source: git
+                address: 'git@repo.domain.com:salt/formula-salt.git'
+                branch: 'master'
+              openssh:
+                source: git
+                address: 'git@repo.domain.com:salt/formula-openssh.git'
+                branch: 'master'
+
+Simple Salt master with base environment and custom states
+
+.. code-block:: yaml
+
+    salt:
+      master:
+        ...
+        environment:
+          base:
+            states:
+            - name: gitlab
+              source: git
+              address: 'git@repo.domain.cz:salt/state-gitlab.git'
+              branch: 'master'
+            formulas:
+            ...
+
+Salt master with reclass ENC
+
+.. code-block:: yaml
+
+    salt:
+      master:
+        enabled: true
+        ...
+        pillar:
+          engine: reclass
+          data_dir: /srv/salt/reclass
+
+Salt master with windows repository
+
+.. code-block:: yaml
+
+    salt:
+      master:
+        enabled: true
+        ...
+        windows_repo:
+          type: git
+          address: 'git@repo.domain.com:salt/win-packages.git'
+
+Salt master with API
+
+.. code-block:: yaml
+
+    salt:
+      master:
+        ...
+      api:
+        enabled: true
+        port: 8000
+
+Salt master with preset minions
+
+.. code-block:: yaml
+
+    salt:
+      master:
+        enabled: true
+        ...
+        minions:
+        - name: 'node1.system.location.domain.com'
+
+Salt master syndicate master of masters
+
+.. code-block:: yaml
+
+    salt:
+      master:
+        enabled: true
+        ...
+        syndic:
+          mode: master
+
+Salt master syndicate (client) master
+
+.. code-block:: yaml
+
+    salt:
+      master:
+        enabled: true
+        ...
+        syndicate:
+          mode: client
+          host: master-master
+
+Salt master with custom handlers
+
+.. code-block:: yaml
+
+    salt:
+      master:
+        enabled: true
+        command_timeout: 5
+        worker_threads: 2
+        environments:
+        - name: base
+          states:
+          - source: git
+            address: 'git@repo.domain.com:salt/state-ubuntu.git'
+            branch: 'master'
+          pillar:
+            source: git
+            address: 'git@repo.domain.com:salt/pillar-demo.git'
+            branch: 'master'
+        handlers:
+          name: logstash
+          type: udp
+          bind:
+            host: 127.0.0.1
+            port: 9999
+      minion:
+        handlers:
+        - engine: udp
+          bind:
+            host: 127.0.0.1
+            port: 9999
+        - engine: zmq
+          bind:
+            host: 127.0.0.1
+            port: 9999
+
+Salt minion
+-----------
+
+Simplest Salt minion
+
+.. code-block:: yaml
+
+    salt:
+      minion:
+        enabled: true
+        master:
+          host: master.domain.com
+
+Multi-master Salt minion
+
+.. code-block:: yaml
+
+    salt:
+      minion:
+        enabled: true
+        masters:
+        -  host: master1.domain.com
+        -  host: master2.domain.com
+
+Salt minion with salt mine options
+
+    salt:
+      minion:
+        enabled: true
+        master:
+          host: master.domain.com
+        mine:
+          interval: 60
+          module:
+            grains.items: []
+            network.interfaces: []
+
+Salt minion with graphing dependencies
+
+.. code-block:: yaml
+
+    salt:
+      minion:
+        enabled: true
+        graph_states: true
+        master:
+          host: master.domain.com
+
+Salt control (cloud/virt)
+-------------------------
+
+Salt cloud with local OpenStack insecure (ignoring SSL cert errors) provider 
+
+.. code-block:: yaml
+
+    salt:
+      control:
+        enabled: true
+        provider:
+          openstack_account:
+            engine: openstack
+            insecure: true
+            region: RegionOne
+            identity_url: 'https://10.0.0.2:35357'
+            tenant: devops
+            user: user
+            password: 'password'
+            fixed_networks:
+            - 123d3332-18be-4d1d-8d4d-5f5a54456554e
+            floating_networks:
+            - public
+            ignore_cidr: 192.168.0.0/16
+
+Salt cloud with Digital Ocean provider
+
+.. code-block:: yaml
+
+    salt:
+      control:
+        enabled: true
+        provider:
+          dony1:
+            engine: digital_ocean
+            region: New York 1
+            client_key: xxxxxxx
+            api_key: xxxxxxx
+
+Salt cloud with cluster definition
+
+.. code-block:: yaml
+
+    salt:
+      control:
+        enabled: true
+        cluster:
+          devops_ase:
+            config:
+              engine: salt
+              host: 147.32.120.1
+            node:
+              proxy1.ase.cepsos.cz:
+                provider: cepsos_devops
+                image: Ubuntu12.04 x86_64
+                size: m1.medium
+              node1.ase.cepsos.cz:
+                provider: cepsos_devops
+                image: Ubuntu12.04 x86_64
+                size: m1.medium
+              node2.ase.cepsos.cz:
+                provider: cepsos_devops
+                image: Ubuntu12.04 x86_64
+                size: m1.medium
+              node3.ase.cepsos.cz:
+                provider: cepsos_devops
+                image: Ubuntu12.04 x86_64
+                size: m1.medium
+
+Usage
+=====
+
+Working with salt-cloud
+
+.. code-block:: bash
+
+    salt-cloud -m /path/to/map --assume-yes
+
+Debug LIBCLOUD for salt-cloud connection
+
+.. code-block:: bash
+
+    export LIBCLOUD_DEBUG=/dev/stderr; salt-cloud --list-sizes provider_name --log-level all
+
+Read more
+=========
+
+* http://salt.readthedocs.org/en/latest/
+* https://github.com/DanielBryan/salt-state-graph
+* http://karlgrz.com/testing-salt-states-rapidly-with-docker/
+* https://mywushublog.com/2013/03/configuration-management-with-salt-stack/
+* http://russell.ballestrini.net/replace-the-nagios-scheduler-and-nrpe-with-salt-stack/
+* https://github.com/saltstack-formulas/salt-formula
+* http://docs.saltstack.com/en/latest/topics/tutorials/multimaster.html
+
+salt-cloud
+----------
+
+* http://www.blog.sandro-mathys.ch/2013/07/setting-user-password-when-launching.html
+* http://cloudinit.readthedocs.org/en/latest/topics/examples.html
+* http://salt-cloud.readthedocs.org/en/latest/topics/install/index.html
+* http://docs.saltstack.com/topics/cloud/digitalocean.html
+* http://salt-cloud.readthedocs.org/en/latest/topics/rackspace.html
+* http://salt-cloud.readthedocs.org/en/latest/topics/map.html
+* http://docs.saltstack.com/en/latest/topics/tutorials/multimaster.html
diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..3b04cfb
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+0.2
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..02a9fbd
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,11 @@
+salt-formula-salt (0.2) trusty; urgency=medium
+
+  * First public release
+
+ -- Filip Pytloun <filip.pytloun@tcpcloud.eu>  Tue, 06 Oct 2015 16:38:53 +0200
+
+salt-formula-salt (0.1) trusty; urgency=medium
+
+  * Initial release
+
+ -- Ales Komarek <ales.komarek@tcpcloud.eu>  Thu, 13 Aug 2015 23:23:41 +0200
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..ec63514
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+9
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..6073f20
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,15 @@
+Source: salt-formula-salt
+Maintainer: Ales Komarek <ales.komarek@tcpcloud.eu>
+Section: admin
+Priority: optional
+Build-Depends: debhelper (>= 9)
+Standards-Version: 3.9.6
+Homepage: http://www.tcpcloud.eu
+Vcs-Browser: https://github.com/tcpcloud/salt-formula-salt
+Vcs-Git: https://github.com/tcpcloud/salt-formula-salt.git
+
+Package: salt-formula-salt
+Architecture: all
+Depends: ${misc:Depends}, salt-master, reclass
+Description: Salt salt formula
+ Install and configure Salt masters and minions.
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..79abd6c
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,15 @@
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: salt-formula-salt
+Upstream-Contact: Ales Komarek <ales.komarek@tcpcloud.eu>
+Source: https://github.com/tcpcloud/salt-formula-salt
+
+Files: *
+Copyright: 2014-2015 tcp cloud a.s.
+License: Apache-2.0
+  Copyright (C) 2014-2015 tcp cloud a.s.
+  .
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  .
+  On a Debian system you can find a copy of this license in
+  /usr/share/common-licenses/Apache-2.0.
diff --git a/debian/docs b/debian/docs
new file mode 100644
index 0000000..d585829
--- /dev/null
+++ b/debian/docs
@@ -0,0 +1,3 @@
+README.rst
+CHANGELOG.rst
+VERSION
diff --git a/debian/install b/debian/install
new file mode 100644
index 0000000..4eb02c4
--- /dev/null
+++ b/debian/install
@@ -0,0 +1,2 @@
+salt/*             /usr/share/salt-formulas/env/salt/
+metadata/service/*      /usr/share/salt-formulas/reclass/service/salt/
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..abde6ef
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,5 @@
+#!/usr/bin/make -f
+
+%:
+	dh $@
+
diff --git a/debian/source/format b/debian/source/format
new file mode 100644
index 0000000..89ae9db
--- /dev/null
+++ b/debian/source/format
@@ -0,0 +1 @@
+3.0 (native)
diff --git a/metadata/service/master/cluster.yml b/metadata/service/master/cluster.yml
new file mode 100644
index 0000000..404a8a9
--- /dev/null
+++ b/metadata/service/master/cluster.yml
@@ -0,0 +1,10 @@
+applications:
+- salt
+- git
+- openssh
+parameters:
+  salt:
+    master:
+      enabled: true
+      command_timeout: 5
+      worker_threads: 2
diff --git a/metadata/service/master/single.yml b/metadata/service/master/single.yml
new file mode 100644
index 0000000..58f437e
--- /dev/null
+++ b/metadata/service/master/single.yml
@@ -0,0 +1,13 @@
+applications:
+- salt
+- git
+- openssh
+parameters:
+  _param:
+    salt_master_base_environment: dev
+  salt:
+    master:
+      enabled: true
+      command_timeout: 5
+      worker_threads: 2
+      base_environment: ${_param:salt_master_base_environment}
\ No newline at end of file
diff --git a/metadata/service/minion/cluster.yml b/metadata/service/minion/cluster.yml
new file mode 100644
index 0000000..fbb64dc
--- /dev/null
+++ b/metadata/service/minion/cluster.yml
@@ -0,0 +1,13 @@
+applications:
+- salt
+parameters:
+  salt:
+    minion:
+      enabled: true
+      master:
+        host: ${_param:salt_master_host}
+      mine:
+        interval: 60
+        module:
+          grains.items: []
+          network.interfaces: []
diff --git a/metadata/service/minion/local.yml b/metadata/service/minion/local.yml
new file mode 100644
index 0000000..6739f17
--- /dev/null
+++ b/metadata/service/minion/local.yml
@@ -0,0 +1,10 @@
+applications:
+- salt
+parameters:
+  salt:
+    minion:
+      enabled: true
+      local: true
+      pillar:
+        engine: reclass
+        data_dir: /srv/salt/reclass
diff --git a/metadata/service/minion/master.yml b/metadata/service/minion/master.yml
new file mode 100644
index 0000000..fbb64dc
--- /dev/null
+++ b/metadata/service/minion/master.yml
@@ -0,0 +1,13 @@
+applications:
+- salt
+parameters:
+  salt:
+    minion:
+      enabled: true
+      master:
+        host: ${_param:salt_master_host}
+      mine:
+        interval: 60
+        module:
+          grains.items: []
+          network.interfaces: []
diff --git a/salt/api.sls b/salt/api.sls
new file mode 100644
index 0000000..5d53d9d
--- /dev/null
+++ b/salt/api.sls
@@ -0,0 +1,21 @@
+{%- from "salt/map.jinja" import api with context %}
+{%- if api.enabled %}
+
+include:
+- salt.master
+
+salt_api_packages:
+  pkg.installed
+  - names: {{ api.pkgs }}
+  - require:
+    - pkg: salt_master_packages
+
+salt_api_service:
+  service.running:
+  - name: salt-api
+  - require:
+    - pkg: salt_api_packages
+  - watch:
+    - file: /etc/salt/master
+
+{%- endif %}
\ No newline at end of file
diff --git a/salt/control/cluster.sls b/salt/control/cluster.sls
new file mode 100644
index 0000000..de9d2a2
--- /dev/null
+++ b/salt/control/cluster.sls
@@ -0,0 +1,47 @@
+{% from "salt/map.jinja" import control with context %}
+{%- if control.enabled %}
+
+/srv/salt/cloud/maps:
+  file.directory:
+  - makedirs: true
+
+/srv/salt/cloud/userdata:
+  file.directory:
+  - makedirs: true
+
+{%- for cluster_name, cluster in control.cluster.iteritems() %}
+
+/srv/salt/cloud/maps/{{ cluster_name }}:
+  file.managed:
+  - source: salt://salt/files/map
+  - user: root
+  - group: root
+  - template: jinja
+  - require:
+    - file: /srv/salt/cloud/maps
+  - defaults:
+      cluster_name: "{{ cluster_name }}"
+
+/srv/salt/cloud/userdata/{{ cluster_name }}:
+  file.directory:
+  - makedirs: true
+
+{%- for node_name, node in cluster.node.iteritems() %}
+
+/srv/salt/cloud/userdata/{{cluster_name }}/{{ node_name }}.conf:
+  file.managed:
+  - source: salt://salt/files/userdata
+  - user: root
+  - group: root
+  - template: jinja
+  - require:
+    - file: /srv/salt/cloud/userdata/{{ cluster_name }}
+  - defaults:
+      cluster_name: "{{ cluster_name }}"
+      node_name: "{{ node_name }}"
+
+{%- endfor %}
+
+{%- endfor %}
+
+{%- endif %}
\ No newline at end of file
diff --git a/salt/control/init.sls b/salt/control/init.sls
new file mode 100644
index 0000000..1feb678
--- /dev/null
+++ b/salt/control/init.sls
@@ -0,0 +1,4 @@
+include:
+- salt.control.service
+- salt.control.provider
+- salt.control.cluster
\ No newline at end of file
diff --git a/salt/control/provider.sls b/salt/control/provider.sls
new file mode 100644
index 0000000..09a5452
--- /dev/null
+++ b/salt/control/provider.sls
@@ -0,0 +1,18 @@
+{% from "salt/map.jinja" import control with context %}
+{%- if control.enabled %}
+
+/etc/salt/cloud.providers:
+  file.managed:
+  - source: salt://salt/files/providers.conf
+  - user: root
+  - group: root
+  - template: jinja
+
+/etc/salt/cloud.profiles:
+  file.managed:
+  - source: salt://salt/files/profiles.conf
+  - user: root
+  - group: root
+  - template: jinja
+
+{%- endif %}
\ No newline at end of file
diff --git a/salt/control/service.sls b/salt/control/service.sls
new file mode 100644
index 0000000..2acab7b
--- /dev/null
+++ b/salt/control/service.sls
@@ -0,0 +1,26 @@
+{% from "salt/map.jinja" import control with context %}
+{%- if control.enabled %}
+
+{%- if control.pkgs is defined and control.pkgs|length > 0 %}
+
+salt_control_packages:
+  pkg.installed:
+    - names: {{ control.pkgs }}
+
+{%- else %}
+{# No system packages defined, install with pip #}
+
+salt_control_packages:
+  pkg.installed:
+  - name: python-pip
+
+{%- for package in control.python_pkgs %}
+{{ package }}:
+  pip.installed:
+  - require:
+    - pkg: salt_control_packages
+{%- endfor %}
+
+{%- endif %}
+
+{%- endif %}
diff --git a/salt/files/map b/salt/files/map
new file mode 100644
index 0000000..90a0888
--- /dev/null
+++ b/salt/files/map
@@ -0,0 +1,8 @@
+{%- from "salt/map.jinja" import control with context %}
+
+{%- set cluster = salt['pillar.get']('salt:control:cluster:'+cluster_name) %}
+
+{%- for node_name, node in cluster.node.iteritems() %}
+{{ node_name }}.{{ cluster.domain }}:
+- {{ node_name }}.{{ cluster.domain }}
+{%- endfor %}
diff --git a/salt/files/master.conf b/salt/files/master.conf
new file mode 100644
index 0000000..48322e9
--- /dev/null
+++ b/salt/files/master.conf
@@ -0,0 +1,120 @@
+{%- from "salt/map.jinja" import master with context %}
+{%- from "linux/map.jinja" import system with context %}
+
+worker_threads: {{ master.worker_threads }}
+timeout: {{ master.command_timeout }}
+
+{%- if master.system is defined %}
+
+file_roots:
+  base:
+  - /srv/salt/env/{{ master.system.environment }}
+  {%- for formula_name, formula in master.system.get('formula', {}).iteritems() %}
+  - /srv/salt/env/{{ master.system.environment }}/{{ formula_name }}
+  {%- endfor %}
+  {{ master.system.environment }}:
+  - /srv/salt/env/{{ master.system.environment }}
+  {%- for formula_name, formula in master.system.get('formula', {}).iteritems() %}
+  - /srv/salt/env/{{ master.system.environment }}/{{ formula_name }}
+  {%- endfor %}
+
+{%- else %}
+
+file_roots:
+  {%- for environment_name, environment in master.get('environment', {}).iteritems() %}
+  {%- if master.base_environment == environment_name %}
+  base:
+  - /srv/salt/env/{{ environment_name }}
+  {%- endif %}
+  {{ environment_name }}:
+  - /srv/salt/env/{{ environment_name }}
+  {%- endfor %}
+
+{%- endif %}
+
+pillar_opts: False
+
+{%- if master.accept_policy == 'open_mode' %}
+open_mode: True
+{%- endif %}
+
+{%- if master.accept_policy == 'auto_accept' %}
+auto_accept: True
+{%- endif %}
+
+{%- if master.pillar.engine == 'salt' %}
+
+pillar_roots:
+  base:
+  - /srv/salt/pillar
+
+{%- endif %}
+
+{%- if master.pillar.engine == 'reclass' %}
+
+reclass: &reclass
+  storage_type: yaml_fs
+  inventory_base_uri: /srv/salt/reclass
+
+ext_pillar:
+  - reclass: *reclass
+
+master_tops:
+  reclass: *reclass
+
+{%- endif %}
+
+{%- if master.acl is defined %}
+
+client_acl:
+  {%- for acl in master.acl %}
+  {{ acl.name }}:
+  {%- for right in acl.rights %}
+  - {{ right }}
+  {%- endfor %}
+  {%- endfor %}
+
+{%- endif %}
+
+{%- if master.bind.api is defined %}
+
+rest_cherrypy:
+  port: {{ master.api.port }}
+  ssl_crt: /etc/ssl/certs/{{ system.name }}.{{ system.domain }}.crt
+  ssl_key: /etc/ssl/private/{{ system.name }}.{{ system.domain }}.key
+  {%- if pillar.halite is defined %}
+  static: /srv/halite/halite
+  app: /srv/halite/halite/index.html
+  {%- endif %}
+  debug: True
+
+{%- 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.syndic is defined %}
+
+{% if master.syndic.mode == 'master' %}
+order_masters: True
+{%- endif %}
+
+{% if master.syndic.mode == 'client' %}
+syndic_master: {{ master.syndic.host }}
+{%- endif %}
+
+{%- endif %}
diff --git a/salt/files/minion.conf b/salt/files/minion.conf
new file mode 100644
index 0000000..fdf9bd7
--- /dev/null
+++ b/salt/files/minion.conf
@@ -0,0 +1,143 @@
+{%- from "salt/map.jinja" import minion with context %}
+{%- from "linux/map.jinja" import system with context %}
+
+{%- if minion.masters is defined %}
+master:
+{%- for master in minion.masters %}
+- {{ master.host }}
+{%- endfor %}
+{%- else %}
+master: {{ minion.master.host }}
+{%- endif %}
+
+id: {{ system.name }}.{{ system.domain }}
+
+grains:
+  {%- if minion.get('manage_roles', True) %}
+  roles:
+  {%- for key, value in pillar.items() %}
+  {%- if key != 'master' and key != 'system' and key != 'public_keys' and key != 'private_keys' and key != 'known_hosts' and key != '__reclass__'  and key != '_secret' %}
+  {%- for subkey, subvalue in value.iteritems() %}
+  {%- if subvalue.enabled is defined %}
+  {%- if subvalue.enabled %}
+  - {{key}}.{{ subkey }}
+  {%- endif %}
+  {%- endif %}
+  {%- endfor %}
+  {%- endif %}
+  {%- endfor %}
+  {%- endif %}
+  services:
+  {%- for key in pillar.__reclass__.applications %}
+  - {{key}}
+  {%- endfor %}
+
+grains_dirs:
+- /var/lib/salt/grains
+
+{%- if minion.mine is defined %}
+
+mine_functions:
+{%- for salt_module, salt_functions in minion.mine.module.iteritems() %}
+  {{ salt_module }}: {{ salt_functions  }}
+{%- endfor %}
+mine_interval: {{ minion.mine.get('interval', 30) }}
+
+{%- endif %}
+
+
+{%- if minion.sentry is defined %}
+sentry_handler:
+{% for server in minion.sentry.servers %}
+  servers:
+  - {{ server }}
+{% endfor %}
+  project: {{ pillar.salt.minion.sentry.project }}
+  public_key: {{ pillar.salt.minion.sentry.public_key }}
+  secret_key: {{ pillar.salt.minion.sentry.secret_key }}
+  {% if pillar.salt.minion.sentry.log_level is defined %}
+  log_level: {{ pillar.salt.minion.sentry.log_level }}
+  {%- endif %}
+{%- endif %}
+
+{%- if pillar.get('mysql', {}).server is defined %}
+mysql.unix_socket: /var/run/mysqld/mysqld.sock
+{%- if pillar.mysql.server.admin is defined %}
+mysql.user: '{{ pillar.mysql.server.admin.user }}'
+mysql.pass: '{{ pillar.mysql.server.admin.password }}'
+{%- else %}
+mysql.user: 'root'
+mysql.pass: ''
+{%- endif %}
+mysql.db: 'mysql'
+mysql.charset: 'utf8'
+{%- endif %}
+
+{%- if pillar.get('galera', {}).master is defined %}
+mysql.unix_socket: /var/run/mysqld/mysqld.sock
+mysql.user: '{{ pillar.galera.master.admin.user }}'
+mysql.pass: '{{ pillar.galera.master.admin.password }}'
+mysql.db: 'mysql'
+mysql.charset: 'utf8'
+{%- endif %}
+
+{%- if pillar.get('mongodb', {}).server is defined %}
+mongodb.host: 'localhost'
+mongodb.port: {{ pillar.mongodb.server.bind.port }}
+mongodb.user: '{{ pillar.mongodb.server.admin.user }}'
+mongodb.password: '{{ pillar.mongodb.server.admin.password }}'
+{%- endif %}
+
+{%- if pillar.get('galera', {}).slave is defined %}
+mysql.unix_socket: /var/run/mysqld/mysqld.sock
+mysql.user: '{{ pillar.galera.slave.admin.user }}'
+mysql.pass: '{{ pillar.galera.slave.admin.password }}'
+mysql.db: 'mysql'
+mysql.charset: 'utf8'
+{%- endif %}
+
+{%- if pillar.get('postgresql', {}).server is defined %}
+postgres.user: 'postgres'
+postgres.pass: ''
+postgres.db: 'template1'
+{%- endif %}
+
+{%- if pillar.get('gitlab', {}).client is defined %}
+gitlab.url: 'https://{{ pillar.gitlab.client.server.host }}/'
+gitlab.token: '{{ pillar.gitlab.client.server.token }}'
+{%- elif pillar.get('gitlab', {}).server is defined %}
+gitlab.url: 'https://{{ pillar.gitlab.server.server_name }}/'
+gitlab.token: '{{ pillar.gitlab.server.token }}'
+{%- endif %}
+
+{%- if pillar.get('keystone', {}).get('server', {'enabled': False } ).enabled %}
+keystone.token: '{{ pillar.keystone.server.service_token }}'
+keystone.endpoint: 'http://{{ pillar.keystone.server.bind.private_address }}:{{ pillar.keystone.server.bind.private_port }}/v2.0'
+{%- elif pillar.get('keystone', {}).get('client', {'enabled': False } ).enabled %}
+{%- if pillar.keystone.client.server.service_token is defined %}
+keystone.token: '{{ pillar.keystone.client.server.service_token }}'
+keystone.endpoint: 'http://{{ pillar.keystone.client.server.host }}:{{ pillar.keystone.client.server.private_port }}/v2.0'
+{%- else %}
+keystone.user: '{{ pillar.keystone.client.server.user }}'
+keystone.password: '{{ pillar.keystone.client.server.password }}'
+keystone.tenant: '{{ pillar.keystone.client.server.tenant }}'
+keystone.auth_url: 'http://{{ pillar.keystone.client.server.host }}:{{ pillar.keystone.client.server.public_port }}/v2.0/'
+{%- 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 %}
\ No newline at end of file
diff --git a/salt/files/profiles.conf b/salt/files/profiles.conf
new file mode 100644
index 0000000..6baa6ff
--- /dev/null
+++ b/salt/files/profiles.conf
@@ -0,0 +1,25 @@
+{% from "salt/map.jinja" import control with context %}
+
+{%- for cluster_name, cluster in control.cluster.iteritems() %}
+
+{%- for node_name, node in cluster.node.iteritems() %}
+
+{{ node_name }}.{{ cluster.domain }}:
+  provider: {{ node.provider }}
+  image: {{ node.image }}
+  size: {{ node.size }}
+  minion:
+    master: {{ cluster.config.host }}
+    id: {{ node_name }}.{{ cluster.domain }}
+  {%- if node.userdata is defined %}
+  userdata_file: /srv/salt/cloud/userdata/{{ cluster_name }}/{{ node_name }}.conf
+  {%- endif %}
+  {%- if 'ubuntu' in node.image|lower %}
+  ssh_username: ubuntu
+  {%- elif 'centos' in node.image|lower %}
+  ssh_username: cloud-user
+  {%- endif %}
+
+{%- endfor %}
+
+{%- endfor %}
diff --git a/salt/files/providers.conf b/salt/files/providers.conf
new file mode 100644
index 0000000..a764137
--- /dev/null
+++ b/salt/files/providers.conf
@@ -0,0 +1,43 @@
+{% from "salt/map.jinja" import control with context %}
+{%- for provider_name, provider in control.provider.iteritems() %}
+
+{{ provider_name }}:
+  provider: {{ provider.engine }}
+  {%- if provider.insecure is defined %}
+  insecure: true
+  {%- endif %}
+  {%- if provider.engine == 'openstack' %}
+  identity_url: '{{ provider.identity_url }}'
+  {%- if provider.compute_name is defined %}
+  compute_name: {{ provider.get('compute_name', 'nova') }}
+  {%- endif %}
+  protocol: ipv4
+  compute_region: {{ provider.region }}
+  tenant: {{ provider.tenant }}
+  user: {{ provider.user }}
+  {%- if provider.api_key is defined %}
+  apikey: {{ provider.api_key }}
+  {%- elif provider.password is defined %}
+  password: {{ provider.password }}
+  {%- endif %}
+  ssh_key_name: salt-cloud
+  ssh_key_file: /root/.ssh/id_rsa
+  ssh_interface: {{ provider.get('interface', 'private') }}_ips
+  networks:
+  - fixed:
+    {%- for net in provider.fixed_networks %}
+    - {{ net }}
+    {%- endfor %}
+  - floating:
+    {%- for net in provider.floating_networks %}
+    - {{ net }}
+    {%- endfor %}
+  {%- if provider.ignore_cidr is defined %}
+  ignore_cidr: {{ provider.ignore_cidr }}
+  {%- endif %}
+  {%- elif provider.engine == 'digital_ocean' %}
+  {#- location: {{ provider.region }} #}
+  personal_access_token: {{ provider.api_key }}
+  {%- endif %}
+
+{%- endfor %}
diff --git a/salt/files/salt-api.wsgi b/salt/files/salt-api.wsgi
new file mode 100644
index 0000000..07bb35f
--- /dev/null
+++ b/salt/files/salt-api.wsgi
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+
+import cherrypy
+
+import sys
+
+sys.path.append('/srv/salt-api')
+
+from saltapi.netapi.rest_cherrypy import app
+
+def bootstrap_app():
+    '''
+    Grab the opts dict of the master config by trying to import Salt
+    '''
+    import salt.client
+    opts = salt.client.LocalClient().opts
+    return app.get_app(opts)
+
+def get_application(*args):
+    '''
+    Returns a WSGI application function. If you supply the WSGI app and config
+    it will use that, otherwise it will try to obtain them from a local Salt
+    installation
+    '''
+    opts_tuple = args
+
+    def wsgi_app(environ, start_response):
+        root, _, conf = opts_tuple or bootstrap_app()
+
+        cherrypy.tree.mount(root, '/', conf)
+        return cherrypy.tree(environ, start_response)
+
+    return wsgi_app
+
+application = get_application()
diff --git a/salt/files/salt-state-graph.py b/salt/files/salt-state-graph.py
new file mode 100644
index 0000000..243d561
--- /dev/null
+++ b/salt/files/salt-state-graph.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python
+"""
+salt-state-graph
+
+A tool that ingests the YAML representing the Salt highstate (or sls state) for
+a single minion and produces a program written in DOT.
+
+The tool is useful for visualising the dependency graph of a Salt highstate.
+"""
+from pydot import Dot, Node, Edge
+import yaml
+import sys
+
+
+def find(obj, find_key):
+    """
+    Takes a list and a set.  Returns a list of all matching objects.
+
+    Uses find_inner to recursively traverse the data structure, finding objects
+    with keyed by find_key.
+    """
+
+    all_matches = [find_inner(item, find_key) for item in obj]
+    final = [item for sublist in all_matches for item in sublist]
+
+    return final
+
+
+def find_inner(obj, find_key):
+    """
+    Recursively search through the data structure to find objects
+    keyed by find_key.
+    """
+    results = []
+
+    if not hasattr(obj, '__iter__'):
+        # not a sequence type - return nothing
+        # this excludes strings
+        return results
+
+    if isinstance(obj, dict):
+        # a dict - check each key
+        for key, prop in obj.iteritems():
+            if key == find_key:
+                results.extend(prop)
+            else:
+                results.extend(find_inner(prop, find_key))
+    else:
+        # a list / tuple - check each item
+        for i in obj:
+            results.extend(find_inner(i, find_key))
+
+    return results
+
+
+def make_node_name(state_type, state_label):
+    return "{0} - {1}".format(state_type.upper(), state_label)
+
+
+def find_edges(states, relname):
+    """
+    Use find() to recursively find objects at keys matching
+    relname, yielding a node name for every result.
+    """
+    try:
+        deps = find(states, relname)
+        for dep in deps:
+            for dep_type, dep_name in dep.iteritems():
+                yield make_node_name(dep_type, dep_name)
+    except AttributeError as e:
+        sys.stderr.write("Bad state: {0}\n".format(str(states)))
+        raise e
+
+
+def main(input):
+    state_obj = yaml.load(input)
+    graph = Dot("states", graph_type='digraph')
+
+    rules = {
+        'require': {'color': 'blue'},
+        'require_in': {'color': 'blue', 'reverse': True},
+        'watch': {'color': 'red'},
+        'watch_in': {'color': 'red', 'reverse': True},
+    }
+
+    for top_key, props in state_obj.iteritems():
+        # Add a node for each state type embedded in this state
+        # keys starting with underscores are not candidates
+
+        if top_key == '__extend__':
+            # TODO - merge these into the main states and remove them
+            sys.stderr.write(
+                    "Removing __extend__ states:\n{0}\n".format(str(props)))
+            continue
+
+        for top_key_type, states in props.iteritems():
+            if top_key_type[:2] == '__':
+                continue
+
+            node_name = make_node_name(top_key_type, top_key)
+            graph.add_node(Node(node_name))
+
+            for edge_type, ruleset in rules.iteritems():
+                for relname in find_edges(states, edge_type):
+                    if 'reverse' in ruleset and ruleset['reverse']:
+                        graph.add_edge(Edge(
+                            node_name, relname, color=ruleset['color']))
+                    else:
+                        graph.add_edge(Edge(
+                            relname, node_name, color=ruleset['color']))
+
+    graph.write('/dev/stdout')
+
+if __name__ == '__main__':
+    main(sys.stdin)
diff --git a/salt/files/salt-state-graph.sh b/salt/files/salt-state-graph.sh
new file mode 100644
index 0000000..a7c0fdb
--- /dev/null
+++ b/salt/files/salt-state-graph.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+salt-call state.show_highstate --out yaml > ./highstate
+python salt-state-graph.py < ./highstate > ./highstate.dot
+dot -Tpng < ./highstate.dot -o ./highstate.png
+dot -Tsvg < ./highstate.dot -o ./highstate.svg
diff --git a/salt/files/salt.gpg b/salt/files/salt.gpg
new file mode 100644
index 0000000..11709fe
--- /dev/null
+++ b/salt/files/salt.gpg
@@ -0,0 +1,13 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: SKS 1.1.4
+Comment: Hostname: keyserver.ubuntu.com
+
+mI0ETs4aowEEALDRpPyebQ5JlJNmleRzZYzxO4Ia8hpCSHzLm6DaMvXcFesfP4OTN0J9JjAv
+xbRxw8ROBz4SZ4iPhzPOZlXHBaI/5BF8Cc8CFW/HDqhPNH3qdOC9Te2q0QZgzd7WS94GXgzK
+nKCi1bmZ5pCvoJEvu3XK24/jmfLijp/1iO46xuofABEBAAG0HExhdW5jaHBhZCBQUEEgZm9y
+IFNhbHQgU3RhY2uIuAQTAQIAIgUCTs4aowIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AA
+CgkQR1n6lg4nwKYQMwP9FQpta7rMNj2kgIvv3pXOr4z+Rgp3Vd8NmoHlIt6ZyigiisGfuht9
+0PyvBLlJxpqGZt7bdz5QzV3HYSkk1K/L6CzpAgC/o/LPiir/Xi4ur/tgmRp30ONGPrLvSyRk
+dv1pIkMqEekOSdTgjyvRq7+rYpqh5obYFJPoxSqDYOND9lo=
+=woTM
+-----END PGP PUBLIC KEY BLOCK-----
\ No newline at end of file
diff --git a/salt/files/sensu.conf b/salt/files/sensu.conf
new file mode 100644
index 0000000..8b2ea59
--- /dev/null
+++ b/salt/files/sensu.conf
@@ -0,0 +1,12 @@
+local_salt_master_proc:
+  command: "PATH=$PATH:/usr/lib64/nagios/plugins:/usr/lib/nagios/plugins check_procs -C salt-master -u root -c 1:10"
+  interval: 60
+  occurrences: 1
+  subscribers:
+  - local-salt-master
+local_salt_minion_proc:
+  command: "PATH=$PATH:/usr/lib64/nagios/plugins:/usr/lib/nagios/plugins check_procs -C salt-minion -u root -c 1:10"
+  interval: 60
+  occurrences: 1
+  subscribers:
+  - local-salt-minion
diff --git a/salt/files/userdata b/salt/files/userdata
new file mode 100644
index 0000000..ac3a286
--- /dev/null
+++ b/salt/files/userdata
@@ -0,0 +1,9 @@
+{%- from "salt/map.jinja" import config with context %}
+{%- set cluster = salt['pillar.get']('salt:control:cluster:'+cluster_name) %}
+{%- set node = salt['pillar.get']('salt:control:cluster:'+cluster_name+':node:'+node_name) %}
+#!/bin/bash
+curl --insecure -L http://bootstrap.saltstack.org -o install_salt.sh
+sh install_salt.sh
+echo "id: {{ node_name }}.{{ cluster.domain }}" > /etc/salt/minion.d/minion.conf
+echo "master: salt/master: {{ cluster.config.host }}" >> /etc/salt/minion.d/minion.conf
+service salt-minion restart
\ No newline at end of file
diff --git a/salt/init.sls b/salt/init.sls
new file mode 100644
index 0000000..d7a67c4
--- /dev/null
+++ b/salt/init.sls
@@ -0,0 +1,15 @@
+{%- if pillar.salt is defined %}
+include:
+{%- if pillar.salt.master is defined %}
+- salt.master
+{%- endif %}
+{%- if pillar.salt.minion is defined %}
+- salt.minion
+{%- endif %}
+{%- if pillar.salt.syndic is defined %}
+- salt.syndic
+{%- endif %}
+{%- if pillar.salt.control is defined %}
+- salt.control
+{%- endif %}
+{%- endif %}
\ No newline at end of file
diff --git a/salt/map.jinja b/salt/map.jinja
new file mode 100644
index 0000000..886404a
--- /dev/null
+++ b/salt/map.jinja
@@ -0,0 +1,120 @@
+
+{% set master = salt['grains.filter_by']({
+    'Arch': {
+        'pkgs': ['salt-zmq'],
+        'service': 'salt-master',
+        'pillar': {
+            'engine': 'salt',
+        },
+        'accept_policy': 'preseed',
+        'bind': {},
+        'formula': {},
+        'base_environment': 'dev',
+    },
+    'Debian': {
+        'pkgs': ['salt-master'],
+        'service': 'salt-master',
+        'pillar': {
+            'engine': 'salt',
+        },
+        'accept_policy': 'preseed',
+        'bind': {},
+        'formula': {},
+        'base_environment': 'dev',
+    },
+    'Gentoo': {
+        'pkgs': ['app-admin/salt'],
+        'service': 'salt-master',
+        'pillar': {
+            'engine': 'salt',
+        },
+        'accept_policy': 'preseed',
+        'bind': {},
+        'formula': {},
+        'base_environment': 'dev',
+    },
+    'MacOS': {
+        'pkgs': ['saltstack'],
+        'service': 'salt-master',
+        'pillar': {
+            'engine': 'salt',
+        },
+        'accept_policy': 'preseed',
+        'bind': {},
+        'formula': {},
+        'base_environment': 'dev',
+    },
+    'RedHat': {
+        'pkgs': ['salt-master'],
+        'service': 'salt-master',
+        'pillar': {
+            'engine': 'salt',
+        },
+        'accept_policy': 'preseed',
+        'bind': {},
+        'formula': {},
+        'base_environment': 'dev',
+    },
+}, merge=salt['pillar.get']('salt:master')) %}
+
+{% set minion = salt['grains.filter_by']({
+    'Arch': {
+        'pkgs': ['salt-zmq'],
+        'graph_pkgs': ['graphviz'],
+        'graph_states_pkgs': ['python-pydot', 'python-yaml'],
+        'graph_states': False,
+        'service': 'salt-minion',
+    },
+    'Debian': {
+        'pkgs': ['salt-minion'],
+        'graph_pkgs': ['graphviz'],
+        'graph_states_pkgs': ['python-pydot', 'python-yaml'],
+        'graph_states': False,
+        'service': 'salt-minion',
+    },
+    'Gentoo': {
+        'pkgs': ['app-admin/salt'],
+        'graph_pkgs': ['graphviz'],
+        'graph_states_pkgs': ['python-pydot', 'python-yaml'],
+        'graph_states': False,
+        'service': 'salt-minion',
+    },
+    'MacOS': {
+        'pkgs': ['saltstack'],
+        'graph_pkgs': ['graphviz'],
+        'graph_states_pkgs': ['python-pydot', 'python-yaml'],
+        'graph_states': False,
+        'service': 'salt-minion',
+    },
+    'RedHat': {
+        'pkgs': ['salt-minion'],
+        'graph_pkgs': ['graphviz'],
+        'graph_states_pkgs': ['python-pydot', 'python-yaml'],
+        'graph_states': False,
+        'service': 'salt-minion',
+    },
+}, merge=salt['pillar.get']('salt:minion')) %}
+
+{% set api = salt['grains.filter_by']({
+    'Debian': {
+        'pkgs': ['salt-api'],
+        'service': 'salt-api',
+    },
+    'RedHat': {
+        'pkgs': ['salt-api'],
+        'service': 'salt-api',
+    },
+}, merge=salt['pillar.get']('salt:api')) %}
+
+{% set control = salt['grains.filter_by']({
+    'Debian': {
+        'python_pkgs': ['apache-libcloud', 'netaddr'],
+        'pkgs': ['python-netaddr', 'python-libcloud'],
+        'cluster': {},
+    },
+    'RedHat': {
+        'python_pkgs': ['apache-libcloud', 'netaddr'],
+        'pkgs': ['python-netaddr', 'python-libcloud'],
+        'cluster': {},
+    },
+}, merge=salt['pillar.get']('salt:control')) %}
diff --git a/salt/master/api.sls b/salt/master/api.sls
new file mode 100644
index 0000000..4583b04
--- /dev/null
+++ b/salt/master/api.sls
@@ -0,0 +1,22 @@
+{%- from "salt/map.jinja" import master with context %}
+{%- if master.enabled %}
+
+include:
+- salt.master.service
+
+salt_api_packages:
+  pkg.latest
+  - names:
+    - salt-api
+  - require:
+    - pkg: salt_master_packages
+
+salt_api_service:
+  service.running:
+  - name: salt-api
+  - require:
+    - pkg: salt_api_packages
+  - watch:
+    - file: /etc/salt/master
+
+{%- endif %}
diff --git a/salt/master/ca.sls b/salt/master/ca.sls
new file mode 100644
index 0000000..83568b7
--- /dev/null
+++ b/salt/master/ca.sls
@@ -0,0 +1,23 @@
+{%- from "salt/map.jinja" import master with context %}
+{%- if master.enabled %}
+
+{%- if pillar.django_pki is defined %}
+{%- if pillar.django_pki.server.enabled %}
+
+include:
+- salt.master.service
+
+{#
+{%- for environment_name, environment in master.environment.iteritems() %}
+
+/srv/salt/env/{{ environment_name }}/pki:
+  file.symlink:
+  - target: /srv/django_pki/site/pki
+
+{%- endfor %}
+#}
+
+{%- endif %}
+{%- endif %}
+
+{%- endif %}
\ No newline at end of file
diff --git a/salt/master/env.sls b/salt/master/env.sls
new file mode 100644
index 0000000..2dbf935
--- /dev/null
+++ b/salt/master/env.sls
@@ -0,0 +1,248 @@
+{%- from "salt/map.jinja" import master with context %}
+{%- if master.enabled %}
+
+include:
+- git.client
+- salt.master.service
+
+{%- if master.system is defined %}
+
+salt_env_{{ master.system.environment }}_dirs:
+  file.directory:
+  - names: 
+    - /srv/salt/env/{{ master.system.environment }}/_modules
+    - /srv/salt/env/{{ master.system.environment }}/_states
+    - /srv/salt/env/{{ master.system.environment }}/_grains
+    - /srv/salt/env/{{ master.system.environment }}
+  - makedirs: True
+
+{%- for grain_name, grain in master.system.get('grain', {}).iteritems() %}
+
+{%- if grain.source == 'git' %}
+
+salt_master_{{ master.system.environment }}_{{ grain_name }}_grain_source:
+  git.latest:
+  - name: {{ grain.address }}
+  - target: /srv/salt/env/{{ master.system.environment }}/_extra/grain_{{ grain_name }}
+  - rev: {{ grain.revision }}
+  - require:
+    - file: salt_env_{{ master.system.environment }}_dirs
+    - pkg: git_packages
+
+/srv/salt/env/{{ master.system.environment }}/_grains/{{ grain_name }}.py:
+  file.symlink:
+  - target: /srv/salt/env/{{ master.system.environment }}/_extra/grain_{{ grain_name }}/{{ grain_name }}.py
+  - require:
+    - git: salt_master_{{ master.system.environment }}_{{ grain_name }}_grain_source
+
+{%- endif %}
+
+{%- endfor %}
+
+{%- for state_name, state in master.system.get('state', {}).iteritems() %}
+
+{%- if state.source == 'git' %}
+
+salt_master_{{ master.system.environment }}_{{ state_name }}_state_source:
+  git.latest:
+  - name: {{ state.address }}
+  - target: /srv/salt/env/{{ master.system.environment }}/_extra/state_{{ state_name }}
+  - rev: {{ state.revision }}
+  - require:
+    - file: salt_env_{{ master.system.environment }}_dirs
+    - pkg: git_packages
+
+/srv/salt/env/{{ master.system.environment }}/_modules/{{ state_name }}.py:
+  file.symlink:
+  - target: /srv/salt/env/{{ master.system.environment }}/_extra/state_{{ state_name }}/modules/{{ state_name }}.py
+  - require:
+    - git: salt_master_{{ master.system.environment }}_{{ state_name }}_state_source
+
+/srv/salt/env/{{ master.system.environment }}/_states/{{ state_name }}.py:
+  file.symlink:
+  - target: /srv/salt/env/{{ master.system.environment }}/_extra/state_{{ state_name }}/states/{{ state_name }}.py
+  - require:
+    - git: salt_master_{{ master.system.environment }}_{{ state_name }}_state_source
+
+{%- endif %}
+
+{%- endfor %}
+
+{%- for formula_name, formula in master.system.get('formula', {}).iteritems() %}
+
+{%- if formula.source == 'git' %}
+
+salt_master_{{ master.system.environment }}_{{ formula_name }}_formula_source:
+  git.latest:
+  - name: {{ formula.address }}
+  - target: /srv/salt/env/{{ master.system.environment }}/{{ formula_name }}
+  - rev: {{ formula.revision }}
+  - require:
+    - file: salt_env_{{ master.system.environment }}_dirs
+    - pkg: git_packages
+
+{%- endif %}
+
+{%- endfor %}
+
+{%- if master.system.returners is defined %}
+
+salt_master_{{ master.system.environment }}_returners:
+  git.latest:
+  - name: {{ master.system.returners.address }}
+  - target: /srv/salt/env/{{ master.system.environment }}/_returners
+  - rev: {{ master.system.returners.revision }}
+  - require:
+    - file: salt_env_{{ master.system.environment }}_dirs
+    - pkg: git_packages
+
+{%- endif %}
+
+{%- endif %}
+
+{# Start new #}
+
+{%- for environment_name, environment in master.get('environment', {}).iteritems() %}
+
+{%- if master.base_environment == environment_name %}
+
+salt_env_{{ environment_name }}_pre_dirs:
+  file.directory:
+  - names: 
+    - /usr/share/salt-formulas/env/_modules
+    - /usr/share/salt-formulas/env/_states
+    - /usr/share/salt-formulas/env/_grains
+    - /usr/share/salt-formulas/env/_formulas
+  - makedirs: True
+
+salt_env_{{ environment_name }}_dirs:
+  file.symlink:
+  - name: /srv/salt/env/{{ environment_name }}
+  - target: /usr/share/salt-formulas/env
+  - require:
+    - file: salt_env_{{ environment_name }}_pre_dirs
+
+{%- else %}
+
+salt_env_{{ environment_name }}_dirs:
+  file.directory:
+  - names: 
+    - /srv/salt/env/{{ environment_name }}/_modules
+    - /srv/salt/env/{{ environment_name }}/_states
+    - /srv/salt/env/{{ environment_name }}/_grains
+    - /srv/salt/env/{{ environment_name }}/_formulas
+  - makedirs: True
+
+{%- endif %}
+
+{%- for formula_name, formula in environment.get('formula', {}).iteritems() %}
+
+{%- if formula.source == 'pkg' %}
+
+salt_master_{{ environment_name }}_{{ formula.name }}_formula:
+  pkg.latest:
+  - name: {{ formula.name }}
+
+{%- elif formula.source == 'git' %}
+
+{%- if master.base_environment == environment_name %}
+
+salt_master_{{ environment_name }}_{{ formula_name }}_formula:
+  git.latest:
+  - name: {{ formula.address }}
+  - target: /usr/share/salt-formulas/env/_formulas/{{ formula_name }}
+  - rev: {{ formula.revision }}
+  - require:
+    - file: salt_env_{{ environment_name }}_dirs
+    - pkg: git_packages
+
+salt_env_{{ environment_name }}_{{ formula_name }}_link:
+  file.symlink:
+  - name: /usr/share/salt-formulas/env/{{ formula_name }}
+  - target: /usr/share/salt-formulas/env/_formulas/{{ formula_name }}/{{ formula_name }}
+  - require:
+    - file: salt_env_{{ environment_name }}_dirs
+
+{%- for grain_name, grain in formula.get('grain', {}).iteritems() %}
+
+salt_master_{{ environment_name }}_{{ grain_name }}_grain:
+  file.symlink:
+  - name: /usr/share/salt-formulas/env/_grains/{{ grain_name }}
+  - target: /usr/share/salt-formulas/env/_formulas/{{ formula_name }}/_grains/{{ grain_name }}
+
+{%- endfor %}
+
+{%- for module_name, module in formula.get('module', {}).iteritems() %}
+
+salt_master_{{ environment_name }}_{{ module_name }}_module:
+  file.symlink:
+  - name: /usr/share/salt-formulas/env/_modules/{{ module_name }}
+  - target: /usr/share/salt-formulas/env/_formulas/{{ formula_name }}/_modules/{{ module_name }}
+
+{%- endfor %}
+
+{%- for state_name, state in formula.get('state', {}).iteritems() %}
+
+salt_master_{{ environment_name }}_{{ state_name }}_state:
+  file.symlink:
+  - name: /usr/share/salt-formulas/env/_states/{{ state_name }}
+  - target: /usr/share/salt-formulas/env/_formulas/{{ formula_name }}/_states/{{ state_name }}
+
+{%- endfor %}
+
+{%- else %}
+
+salt_master_{{ environment_name }}_{{ formula_name }}_formula:
+  git.latest:
+  - name: {{ formula.address }}
+  - target: /srv/salt/env/{{ environment_name }}/_formulas/{{ formula_name }}
+  - rev: {{ formula.revision }}
+  - require:
+    - file: salt_env_{{ environment_name }}_dirs
+    - pkg: git_packages
+
+salt_env_{{ environment_name }}_{{ formula_name }}_link:
+  file.symlink:
+  - name: /srv/salt/env/{{ environment_name }}/{{ formula_name }}
+  - target: /srv/salt/env/{{ environment_name }}/_formulas/{{ formula_name }}/{{ formula_name }}
+  - require:
+    - file: salt_env_{{ environment_name }}_dirs
+
+{%- for grain_name, grain in formula.get('grain', {}).iteritems() %}
+
+salt_master_{{ environment_name }}_{{ grain_name }}_grain:
+  file.symlink:
+  - name: /srv/salt/env/{{ environment_name }}/_grains/{{ grain_name }}
+  - target: /srv/salt/env/{{ environment_name }}/_formulas/{{ formula_name }}/_grains/{{ grain_name }}
+
+{%- endfor %}
+
+{%- for module_name, module in formula.get('module', {}).iteritems() %}
+
+salt_master_{{ environment_name }}_{{ module_name }}_module:
+  file.symlink:
+  - name: /srv/salt/env/{{ environment_name }}/_grains/{{ module_name }}
+  - target: /srv/salt/env/{{ environment_name }}/_formulas/{{ formula_name }}/_grains/{{ module_name }}
+
+{%- endfor %}
+
+{%- for state_name, state in formula.get('state', {}).iteritems() %}
+
+salt_master_{{ environment_name }}_{{ state_name }}_state:
+  file.symlink:
+  - name: /srv/salt/env/{{ environment_name }}/_grains/{{ state_name }}
+  - target: /srv/salt/env/{{ environment_name }}/_formulas/{{ formula_name }}/_grains/{{ state_name }}
+
+{%- endfor %}
+
+{%- endif %}
+
+{%- endif %}
+
+{%- endfor %}
+
+{%- endfor %}
+
+{# end new #}
+
+{%- endif %}
\ No newline at end of file
diff --git a/salt/master/init.sls b/salt/master/init.sls
new file mode 100644
index 0000000..b4e4142
--- /dev/null
+++ b/salt/master/init.sls
@@ -0,0 +1,7 @@
+include:
+- salt.master.service
+- salt.master.env
+- salt.master.pillar
+- salt.master.minion
+- salt.master.win_repo
+- salt.master.ca
\ No newline at end of file
diff --git a/salt/master/minion.sls b/salt/master/minion.sls
new file mode 100644
index 0000000..8ba4fea
--- /dev/null
+++ b/salt/master/minion.sls
@@ -0,0 +1,46 @@
+{%- from "salt/map.jinja" import master with context %}
+{%- if master.enabled %}
+
+include:
+- salt.master.service
+
+{%- if master.minion is defined %}
+
+/srv/salt/minion_keys:
+  file.directory:
+  - makedirs: true
+  - require:
+    - pkg: salt_master_packages
+
+{%- for name, environment in master.environment.iteritems() %}
+
+/srv/salt/env/{{ name }}/minion_keys:
+  file.symlink:
+  - target: /srv/salt/minion_keys
+  - require:
+    - file: /srv/salt/env/{{ name }}
+    - file: /srv/salt/minion_keys
+
+{%- endfor %}
+
+{%- for minion in master.minion %}
+
+generate_key_{{ minion.name }}:
+  cmd.run:
+  - name: salt-key --gen-keys={{ minion.name }} --gen-keys-dir=/srv/salt/minion_keys
+  - unless: "test -e /srv/salt/minion_keys/{{ minion.name}}.pem"
+  - require:
+    - file: /srv/salt/minion_keys
+
+copy_generated_key_{{ minion.name }}:
+  cmd.run:
+  - name: cp /srv/salt/minion_keys/{{ minion.name }}.pub /etc/salt/pki/master/minions/{{ minion.name }}
+  - unless: "test -e /etc/salt/pki/master/minions/{{ minion.name }}"
+  - require:
+    - cmd: generate_key_{{ minion.name }}
+
+{%- endfor %}
+
+{%- endif %}
+
+{%- endif %}
\ No newline at end of file
diff --git a/salt/master/pillar.sls b/salt/master/pillar.sls
new file mode 100644
index 0000000..c7be5d5
--- /dev/null
+++ b/salt/master/pillar.sls
@@ -0,0 +1,76 @@
+{%- from "salt/map.jinja" import master with context %}
+{%- if master.enabled %}
+
+{%- if master.pillar.engine == 'salt' %}
+
+include:
+- git.client
+- salt.master.service
+
+{{ master.pillar.source.address }}:
+  git.latest:
+  - target: /srv/salt/pillar
+  - rev: {{ master.pillar.source.branch }}
+  - require:
+    - file: /srv/salt/env
+    - pkg: git_packages
+
+/srv/salt/env/{{ master.system.environment }}/top.sls:
+  file.symlink:
+  - target: /srv/salt/pillar/files_top.sls
+  - require:
+    - file: /srv/salt/env/{{ master.system.environment }}
+
+{%- elif master.pillar.engine == 'reclass' %}
+
+include:
+- reclass.storage.data
+
+/srv/salt/reclass/classes/service:
+  file.directory
+
+
+{%- if master.system is defined %}
+
+{%- for formula_name, formula in master.system.get('formula', {}).iteritems() %}
+
+/srv/salt/reclass/classes/service/{{ formula_name }}:
+  file.symlink:
+  - target: /srv/salt/env/{{ master.system.environment }}/{{ formula_name }}/metadata/service
+  - require:
+    - git: reclass_data_source
+    - file: /srv/salt/reclass/classes/service
+
+{%- endfor %}
+
+{%- else %}
+
+{%- for environment_name, environment in master.environment.iteritems() %}
+
+{%- for formula_name, formula in environment.get('formula', {}).iteritems() %}
+
+{%- if environment_name == master.base_environment %}
+
+/srv/salt/reclass/classes/service/{{ formula_name }}:
+  file.symlink:
+  {%- if formula.source == 'pkg' %}
+  - target: /usr/share/salt-formulas/reclass/service/{{ formula_name }}
+  {%- else %}
+  - target: /usr/share/salt-formulas/env/_formulas/{{ formula_name }}/metadata/service
+  {%- endif %}
+  - require:
+    - git: reclass_data_source
+    - file: /srv/salt/reclass/classes/service
+
+{%- endif %}
+
+{%- endfor %}
+
+{%- endfor %}
+
+{%-endif %}
+
+
+{%- endif %}
+
+{%- endif %}
\ No newline at end of file
diff --git a/salt/master/service.sls b/salt/master/service.sls
new file mode 100644
index 0000000..4fd05aa
--- /dev/null
+++ b/salt/master/service.sls
@@ -0,0 +1,29 @@
+{%- from "salt/map.jinja" import master with context %}
+{%- if master.enabled %}
+
+salt_master_packages:
+  pkg.latest:
+  - names: {{ master.pkgs }}
+
+/etc/salt/master.d/master.conf:
+  file.managed:
+  - source: salt://salt/files/master.conf
+  - user: root
+  - template: jinja
+  - require:
+    - pkg: salt_master_packages
+  - watch_in:
+    - service: salt_master_service
+
+salt_master_service:
+  service.running:
+  - name: {{ master.service }}
+  - enable: true
+
+/srv/salt/env:
+  file.directory:
+  - user: root
+  - mode: 755
+  - makedirs: true
+
+{%- endif %}
diff --git a/salt/master/win_repo.sls b/salt/master/win_repo.sls
new file mode 100644
index 0000000..ef41129
--- /dev/null
+++ b/salt/master/win_repo.sls
@@ -0,0 +1,49 @@
+{%- from "salt/map.jinja" import master with context %}
+{%- if master.enabled %}
+
+include:
+- git.client
+- salt.master.service
+
+{%- if master.windows_repo is defined %}
+
+/srv/salt/win:
+  file.directory:
+  - user: root
+  - mode: 755
+  - makedirs: true
+  - require:
+    - file: /srv/salt/env
+
+{%- if master.windows_repo.source == 'git' %}
+
+{{ master.windows_repo.address }}:
+  git.latest:
+  - target: /srv/salt/win/repo
+  - rev: {{ master.windows_repo.branch }}
+  - require:
+    - file: /srv/salt/win
+    - pkg: git_packages
+
+salt_master_update_win_repo:
+  cmd.run:
+  - name: salt-run winrepo.genrepo
+  - require:
+    - git: {{ master.windows_repo.address }}
+
+{%- for environment in master.environments %}
+
+/srv/salt/env/{{ name }}/win:
+  file.symlink:
+  - target: /srv/salt/win
+  - require:
+    - file: /srv/salt/env/{{ name }}
+    - git: {{ master.windows_repo.address }}
+
+{%- endfor %}
+
+{%- endif %}
+
+{%- endif %}
+
+{%- endif %}
\ No newline at end of file
diff --git a/salt/minion.sls b/salt/minion.sls
new file mode 100644
index 0000000..4900389
--- /dev/null
+++ b/salt/minion.sls
@@ -0,0 +1,58 @@
+{%- from "salt/map.jinja" import minion with context %}
+{%- if minion.enabled %}
+
+salt_minion_packages:
+  pkg.latest:
+  - names: {{ minion.pkgs }}
+
+salt_minion_grains_dir:
+  file.directory:
+  - name: /var/lib/salt/grains
+  - mode: 700
+  - makedirs: true
+  - user: root
+
+/etc/salt/minion.d/minion.conf:
+  file.managed:
+  - source: salt://salt/files/minion.conf
+  - user: root
+  - group: root
+  - template: jinja
+  - require:
+    - pkg: salt_minion_packages
+    - file: salt_minion_grains_dir
+  - watch_in:
+    - service: salt_minion_service
+
+salt_minion_service:
+  service.running:
+  - name: {{ minion.service }}
+  - enable: true
+
+{%- if minion.graph_states %}
+
+salt_graph_packages:
+  pkg.latest:
+  - names: {{ minion.graph_pkgs }}
+  - require:
+    - pkg: salt_minion_packages
+
+salt_graph_states_packages:
+  pkg.latest:
+  - names: {{ minion.graph_states_pkgs }}
+
+/root/salt-state-graph.py:
+  file.managed:
+  - source: salt://salt/files/salt-state-graph.py
+  - require:
+    - pkg: salt_graph_packages
+
+/root/salt-state-graph.sh:
+  file.managed:
+  - source: salt://salt/files/salt-state-graph.sh
+  - require:
+    - pkg: salt_graph_packages
+
+{%- endif %}
+
+{%- endif %}
\ No newline at end of file
diff --git a/salt/virt/init.sls b/salt/virt/init.sls
new file mode 100644
index 0000000..5811e85
--- /dev/null
+++ b/salt/virt/init.sls
@@ -0,0 +1,44 @@
+
+wlan0:
+  network.managed:
+  - enabled: True
+  - type: eth
+  - bridge: br0
+
+br0:
+  network.managed:
+  - enabled: True
+  - type: bridge
+  - proto: dhcp
+  - require:
+    - network: wlan0
+
+libvirt:
+  pkg.installed: []
+  file.managed:
+    - name: /etc/sysconfig/libvirtd
+    - contents: 'LIBVIRTD_ARGS="--listen"'
+    - require:
+      - pkg: libvirt
+  libvirt.keys:
+    - require:
+      - pkg: libvirt
+  service.running:
+    - name: libvirtd
+    - require:
+      - pkg: libvirt
+      - network: br0
+      - libvirt: libvirt
+    - watch:
+      - file: libvirt
+
+libvirt-python:
+  pkg.installed: []
+
+libguestfs:
+  pkg.installed:
+    - pkgs:
+      - libguestfs
+      - libguestfs-tools
+
+