Merge pull request #136 from alexandruavadanii/network-routes-noifupdown

routes: Skip network restart on 'noifupdown'
diff --git a/.kitchen.travis.yml b/.kitchen.travis.yml
deleted file mode 100644
index a1d3e04..0000000
--- a/.kitchen.travis.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-suites:
-
-  - name: <%= ENV['SUITE'] %>
-    provisioner:
-      pillars-from-files:
-        linux.sls: tests/pillar/<%= ENV['SUITE'] %>.sls
diff --git a/.kitchen.yml b/.kitchen.yml
index f3fc5b6..c704da5 100644
--- a/.kitchen.yml
+++ b/.kitchen.yml
@@ -29,9 +29,9 @@
   sudo: true
 
 platforms:
-  - name: <%=ENV['PLATFORM'] || 'ubuntu-xenial'%>
+  - name: <%=ENV['PLATFORM'] ||  'saltstack-ubuntu-xenial-salt-stable' %>
     driver_config:
-      image: <%=ENV['PLATFORM'] || 'trevorj/salty-whales:xenial'%>
+      image: <%=ENV['PLATFORM'] || 'epcim/salt:saltstack-ubuntu-xenial-salt-stable'%>
       platform: ubuntu
 
 
diff --git a/.travis.yml b/.travis.yml
index 3e54539..78246a5 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -22,19 +22,24 @@
   - bundle install
 
 env:
-  - PLATFORM=trevorj/salty-whales:trusty SUITE=network
-  - PLATFORM=trevorj/salty-whales:xenial SUITE=network
-  # - PLATFORM=trevorj/salty-whales:trusty SUITE=storage
-  # - PLATFORM=trevorj/salty-whales:xenial SUITE=storage
-  - PLATFORM=trevorj/salty-whales:trusty SUITE=system
-  - PLATFORM=trevorj/salty-whales:xenial SUITE=system
+  - PLATFORM=epcim/salt:saltstack-ubuntu-xenial-salt-2016.3 SUITE=network
+  - PLATFORM=epcim/salt:saltstack-ubuntu-xenial-salt-2016.3 SUITE=system
+  - PLATFORM=epcim/salt:saltstack-ubuntu-xenial-salt-2017.7 SUITE=network
+  - PLATFORM=epcim/salt:saltstack-ubuntu-xenial-salt-2017.7 SUITE=system
+  - PLATFORM=epcim/salt:saltstack-ubuntu-xenial-salt-2018.3 SUITE=network
+  - PLATFORM=epcim/salt:saltstack-ubuntu-xenial-salt-2018.3 SUITE=system
+  # - PLATFORM=epcim/salt:saltstack-ubuntu-bionic-salt-2017.7 SUITE=network
+  # - PLATFORM=epcim/salt:saltstack-ubuntu-bionic-salt-2017.7 SUITE=system
+  # - PLATFORM=epcim/salt:saltstack-ubuntu-bionic-salt-2018.3 SUITE=network
+  # - PLATFORM=epcim/salt:saltstack-ubuntu-bionic-salt-2018.3 SUITE=system
 
 before_script:
   - set -o pipefail
   - make test | tail
 
 script:
-  - KITCHEN_LOCAL_YAML=.kitchen.travis.yml bundle exec kitchen test -t tests/integration
+  - test ! -e .kitchen.yml || bundle exec kitchen converge ${SUITE} || true
+  - test ! -e .kitchen.yml || bundle exec kitchen verify ${SUITE} -t tests/integration
 
 notifications:
   webhooks:
diff --git a/Makefile b/Makefile
index 4977031..d166862 100644
--- a/Makefile
+++ b/Makefile
@@ -27,6 +27,7 @@
 
 all:
 	@echo "make install - Install into DESTDIR"
+	@echo "make lint    - Run lint tests"
 	@echo "make test    - Run tests"
 	@echo "make kitchen - Run Kitchen CI tests (create, converge, verify)"
 	@echo "make clean   - Cleanup after tests run"
@@ -45,6 +46,9 @@
 	[ -d $(DESTDIR)/$(RECLASSDIR)/service/$(FORMULANAME) ] || mkdir -p $(DESTDIR)/$(RECLASSDIR)/service/$(FORMULANAME)
 	cp -a metadata/service/* $(DESTDIR)/$(RECLASSDIR)/service/$(FORMULANAME)
 
+lint:
+	[ ! -d tests ] || (cd tests; ./run_tests.sh lint)
+
 test:
 	[ ! -d tests ] || (cd tests; ./run_tests.sh)
 
diff --git a/README.rst b/README.rst
index b90c0f5..2765990 100644
--- a/README.rst
+++ b/README.rst
@@ -341,6 +341,54 @@
             mode: 700
             makedirs: true
 
+Ensure presence of file by specifying it's source:
+
+.. code-block:: yaml
+
+    linux:
+      system:
+        file:
+          /tmp/test.txt:
+            source: http://example.com/test.txt
+            user: root #optional
+            group: root #optional
+            mode: 700 #optional
+            dir_mode: 700 #optional
+            encoding: utf-8 #optional
+            hash: <<hash>> or <<URI to hash>> #optional
+            makedirs: true #optional
+
+    linux:
+      system:
+        file:
+          test.txt:
+            name: /tmp/test.txt
+            source: http://example.com/test.txt
+
+Ensure presence of file by specifying it's contents:
+
+.. code-block:: yaml
+
+    linux:
+      system:
+        file:
+          /tmp/test.txt:
+            contents: |
+              line1
+              line2
+
+    linux:
+      system:
+        file:
+          /tmp/test.txt:
+            contents_pillar: linux:network:hostname
+
+    linux:
+      system:
+        file:
+          /tmp/test.txt:
+            contents_grains: motd
+
 Kernel
 ~~~~~~
 
@@ -407,6 +455,18 @@
             net.ipv4.tcp_keepalive_time: 30
             net.ipv4.tcp_keepalive_probes: 8
 
+Configure kernel boot options:
+
+.. code-block:: yaml
+
+    linux:
+      system:
+        kernel:
+          boot_options:
+            - elevator=deadline
+            - spectre_v2=off
+            - nopti
+
 
 CPU
 ~~~
@@ -420,6 +480,68 @@
         cpu:
           governor: performance
 
+
+CGROUPS
+~~~~~~~
+
+Setup linux cgroups:
+
+.. code-block:: yaml
+
+    linux:
+      system:
+        cgroup:
+          enabled: true
+          group:
+            ceph_group_1:
+              controller:
+                cpu:
+                  shares:
+                    value: 250
+                cpuacct:
+                  usage:
+                    value: 0
+                cpuset:
+                  cpus:
+                    value: 1,2,3
+                memory:
+                  limit_in_bytes:
+                    value: 2G
+                  memsw.limit_in_bytes:
+                    value: 3G
+              mapping:
+                subjects:
+                - '@ceph'
+            generic_group_1:
+              controller:
+                cpu:
+                  shares:
+                    value: 250
+                cpuacct:
+                  usage:
+                    value: 0
+              mapping:
+                subjects:
+                - '*:firefox'
+                - 'student:cp'
+
+
+Shared Libraries
+~~~~~~~~~~~~~~~~
+
+Set additional shared library to Linux system library path
+
+.. code-block:: yaml
+
+    linux:
+      system:
+        ld:
+          library:
+            java:
+              - /usr/lib/jvm/jre-openjdk/lib/amd64/server
+              - /opt/java/jre/lib/amd64/server
+    
+
 Certificates
 ~~~~~~~~~~~~
 
@@ -481,7 +603,7 @@
               size: 2M
               count: 107520
               mount_point: /mnt/hugepages_2MB
-              mount: false/true # default false
+              mount: false/true # default is true (mount immediately) / false (just save in the fstab)
             large:
               default: true # default automatically mounted
               size: 1G
@@ -700,12 +822,55 @@
         bash:
           preserve_history: true
 
+Login banner message
+~~~~~~~~~~~~~~~~~~~~
+
+/etc/issue is a text file which contains a message or system
+identification to be printed before the login prompt.  It may contain
+various @char and \char sequences, if supported by the getty-type
+program employed on the system.
+
+Setting logon banner message is easy:
+
+.. code-block:: yaml
+
+    liunx:
+      system:
+        banner:
+          enabled: true
+          contents: |
+            UNAUTHORIZED ACCESS TO THIS SYSTEM IS PROHIBITED
+
+            You must have explicit, authorized permission to access or configure this
+            device. Unauthorized attempts and actions to access or use this system may
+            result in civil and/or criminal penalties.
+            All activities performed on this system are logged and monitored.
+
 Message of the day
 ~~~~~~~~~~~~~~~~~~
 
-``pam_motd`` from package ``update-motd`` is used for dynamic messages of the
+``pam_motd`` from package ``libpam-modules`` is used for dynamic messages of the
 day. Setting custom motd will cleanup existing ones.
 
+Setting static motd will replace existing ``/etc/motd`` and remove scripts from
+``/etc/update-motd.d``.
+
+Setting static motd:
+
+.. code-block:: yaml
+
+    linux:
+      system:
+        motd: |
+          UNAUTHORIZED ACCESS TO THIS SYSTEM IS PROHIBITED
+
+          You must have explicit, authorized permission to access or configure this
+          device. Unauthorized attempts and actions to access or use this system may
+          result in civil and/or criminal penalties.
+          All activities performed on this system are logged and monitored.
+
+Setting dynamic motd:
+
 .. code-block:: yaml
 
     linux:
@@ -753,6 +918,18 @@
           logpath: "/var/log/atop"
           outfile: "/var/log/atop/daily.log"
 
+Linux with mcelog service:
+
+.. code-block:: yaml
+
+    linux:
+      system:
+        mcelog:
+          enabled: true
+          logging:
+            syslog: true
+            syslog_error: true
+
 RHEL / CentOS
 ^^^^^^^^^^^^^
 
@@ -993,6 +1170,27 @@
             mtu: 9100
             ipflush_onchange: true
 
+Debian static proto interfaces
+
+When you are changing interface proto from dhcp in up state to static, you
+may need to flush ip addresses and restart interface to assign ip address from a managed file.
+For example, if you want to use the interface and the ip on the bridge.
+This can be done by setting the ``ipflush_onchange`` with combination
+``restart_on_ipflush`` param set to to true.
+
+.. code-block:: yaml
+
+    linux:
+      network:
+        interface:
+          eth1:
+            enabled: true
+            type: eth
+            proto: static
+            address: 10.1.0.22
+            netmask: 255.255.255.0
+            ipflush_onchange: true
+            restart_on_ipflush: true
 
 Concatinating and removing interface files
 
@@ -1335,6 +1533,60 @@
             enabled: true
             type: dpdk_ovs_bridge
 
+**DPDK OVS LACP Bond with vlan tag**
+
+.. code-block:: yaml
+
+    linux:
+      network:
+        bridge: openvswitch
+        dpdk:
+          enabled: true
+          driver: uio
+        openvswitch:
+          pmd_cpu_mask: "0x6"
+          dpdk_socket_mem: "1024,1024"
+          dpdk_lcore_mask: "0x400"
+          memory_channels: "2"
+        interface:
+          eth3:
+            enabled: true
+            type: eth
+            proto: manual
+            name: ${_param:tenant_first_nic}
+          eth4:
+            enabled: true
+            type: eth
+            proto: manual
+            name: ${_param:tenant_second_nic}
+          dpdk0:
+            name: ${_param:tenant_first_nic}
+            pci: "0000:81:00.0"
+            driver: igb_uio
+            bond: bond1
+            enabled: true
+            type: dpdk_ovs_port
+            n_rxq: 2
+          dpdk1:
+            name: ${_param:tenant_second_nic}
+            pci: "0000:81:00.1"
+            driver: igb_uio
+            bond: bond1
+            enabled: true
+            type: dpdk_ovs_port
+            n_rxq: 2
+          bond1:
+            enabled: true
+            bridge: br-prv
+            type: dpdk_ovs_bond
+            mode: balance-slb
+          br-prv:
+            enabled: true
+            type: dpdk_ovs_bridge
+            tag: ${_param:tenant_vlan}
+            address: ${_param:tenant_address}
+            netmask: ${_param:tenant_network_netmask}
+
 **DPDK OVS bridge for VXLAN**
 
 If VXLAN is used as tenant segmentation then ip address must be set on br-prv
@@ -1350,8 +1602,28 @@
             type: dpdk_ovs_bridge
             address: 192.168.50.0
             netmask: 255.255.255.0
+            tag: 101
             mtu: 9000
 
+
+
+**DPDK OVS bridge with Linux network interface**
+
+.. code-block:: yaml
+
+    linux:
+      network:
+        ...
+        interface:
+          eth0:
+            type: eth
+            ovs_bridge: br-prv
+            ...
+          br-prv:
+            enabled: true
+            type: dpdk_ovs_bridge
+            ...
+
 Linux storage
 -------------
 
@@ -1525,6 +1797,32 @@
             - fujitsu_eternus_dxl
             - hitachi_vsp1000
 
+PAM LDAP integration
+
+.. code-block:: yaml
+
+    parameters:
+      linux:
+        system:
+          auth:
+            enabled: true
+            mkhomedir:
+              enabled: true
+              umask: 0027
+            ldap:
+              enabled: true
+              binddn: cn=bind,ou=service_users,dc=example,dc=com
+              bindpw: secret
+              uri: ldap://127.0.0.1
+              base: ou=users,dc=example,dc=com
+              ldap_version: 3
+              pagesize: 65536
+              referrals: off
+              filter:
+                passwd: (&(&(objectClass=person)(uidNumber=*))(unixHomeDirectory=*))
+                shadow: (&(&(objectClass=person)(uidNumber=*))(unixHomeDirectory=*))
+                group:  (&(objectClass=group)(gidNumber=*))
+
 Disabled multipath (the default setup)
 
 .. code-block:: yaml
diff --git a/debian/control b/debian/control
index d634b75..9667ad4 100644
--- a/debian/control
+++ b/debian/control
@@ -10,6 +10,6 @@
 
 Package: salt-formula-linux
 Architecture: all
-Depends: ${misc:Depends}, salt-master, reclass
+Depends: ${misc:Depends}
 Description: Linux salt formula
  Configure Linux operating system.
diff --git a/linux/files/apt.conf b/linux/files/apt.conf
index 709fbd6..f685f3d 100644
--- a/linux/files/apt.conf
+++ b/linux/files/apt.conf
@@ -1,7 +1,7 @@
 // apt.conf file managed by salt-minion
 // DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
 
-{%- for key, value in config.iteritems() %}
+{%- for key, value in config.items() %}
 {{ key }} {% if value is iterable and value is not string %}{ {% for entry in value %}"{{ entry }}";{% endfor %} } {% else %}{{ value }};{% endif %}
 {%- endfor %}
 {#-
diff --git a/linux/files/atop.systemd b/linux/files/atop.service
similarity index 100%
rename from linux/files/atop.systemd
rename to linux/files/atop.service
diff --git a/linux/files/cgconfig.conf b/linux/files/cgconfig.conf
new file mode 100644
index 0000000..6074160
--- /dev/null
+++ b/linux/files/cgconfig.conf
@@ -0,0 +1,16 @@
+{%- from "linux/map.jinja" import system with context -%}
+##
+## This is cgconfig configuration file is managed by Salt
+##
+{%- for cgroup_name, cg in system.cgroup.group.items() %}
+group {{ cgroup_name }} {
+  {%- for controller_name, controller in cg.controller.items() %}
+        {{ controller_name }} {
+    {%- for v_name, v in controller.items() %}
+                {{ controller_name }}.{{ v_name }}="{{ v.value }}";
+    {%- endfor %}
+
+        }
+  {%- endfor %}
+}
+{%- endfor %}
diff --git a/linux/files/cgrules.conf b/linux/files/cgrules.conf
new file mode 100644
index 0000000..aee7726
--- /dev/null
+++ b/linux/files/cgrules.conf
@@ -0,0 +1,10 @@
+{%- from "linux/map.jinja" import system with context -%}
+##
+## This is cgrules configuration file is managed by Salt
+##
+#<user/group>         <controller(s)>         <cgroup>
+{%- for cgroup_name, cg in system.cgroup.group.items() %}
+{%- for subject in cg.mapping.subjects %}
+{{ subject }}{% raw %}         {% endraw %}{%- for controller_name, controller in cg.controller.items() -%}{{ controller_name }}{%- if not loop.last -%},{%- endif -%}{%- endfor -%}{% raw %}         {% endraw %}{{ cgroup_name }}
+{%- endfor %}
+{%- endfor %}
diff --git a/linux/files/dhclient.conf b/linux/files/dhclient.conf
index 1f767c6..bab0de8 100644
--- a/linux/files/dhclient.conf
+++ b/linux/files/dhclient.conf
@@ -95,7 +95,7 @@
 option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;
 {{ render_section(dhclient) }}
 {%- if dhclient.get("interface") -%}
-{%- for iface_name, options in dhclient.interface.iteritems() %}
+{%- for iface_name, options in dhclient.interface.items() %}
 {%- if network.interface.get(iface_name) and network.interface.get(iface_name).enabled == True
    and network.interface.get(iface_name).proto == 'dhcp' -%}
 interface "{{ iface_name }}" {
diff --git a/linux/files/dpdk_interfaces b/linux/files/dpdk_interfaces
index 97ebf30..688c889 100644
--- a/linux/files/dpdk_interfaces
+++ b/linux/files/dpdk_interfaces
@@ -1,5 +1,5 @@
 {%- from "linux/map.jinja" import network with context %}
-{%- for interface_name, interface in network.interface.iteritems() %}
+{%- for interface_name, interface in network.interface.items() %}
 {%- if 'dpdk' in interface.type and interface.pci is defined %}
 pci {{ interface.pci }} {{ interface.driver }}
 {%- endif %}
diff --git a/linux/files/etc_environment b/linux/files/etc_environment
index 0ffa7e1..a09dbc6 100644
--- a/linux/files/etc_environment
+++ b/linux/files/etc_environment
@@ -1,5 +1,5 @@
 
-{%- for name,value in variables.iteritems() if not name.lower().endswith('_proxy') %}
+{%- for name,value in variables.items() if not name.lower().endswith('_proxy') %}
 
 {%- if value is sequence and value is not string %}
 {{ name }}="{{ value|join(':') }}"
diff --git a/linux/files/grafana_dashboards/bond_prometheus.json b/linux/files/grafana_dashboards/bond_prometheus.json
new file mode 100644
index 0000000..706ff8e
--- /dev/null
+++ b/linux/files/grafana_dashboards/bond_prometheus.json
@@ -0,0 +1,360 @@
+{% raw %}
+{
+  "annotations": {
+    "list": []
+  },
+  "editable": true,
+  "gnetId": null,
+  "graphTooltip": 0,
+  "hideControls": false,
+  "id": null,
+  "links": [],
+  "rows": [
+    {
+      "collapse": false,
+      "height": 333,
+      "panels": [
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": "prometheus",
+          "decimals": 0,
+          "fill": 1,
+          "id": 1,
+          "legend": {
+            "avg": false,
+            "current": true,
+            "max": false,
+            "min": false,
+            "show": true,
+            "total": false,
+            "values": true
+          },
+          "lines": true,
+          "linewidth": 1,
+          "links": [],
+          "nullPointMode": "null",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "span": 6,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "bond_slave_failures{host=\"$host\"}",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "{{ bond}}: {{ interface }}",
+              "refId": "A",
+              "step": 2
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "Bond slave failures count",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "transparent": false,
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "decimals": 0,
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            },
+            {
+              "decimals": null,
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ]
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": "prometheus",
+          "decimals": 0,
+          "fill": 1,
+          "id": 2,
+          "legend": {
+            "alignAsTable": false,
+            "avg": false,
+            "current": true,
+            "max": false,
+            "min": false,
+            "show": true,
+            "total": false,
+            "values": true
+          },
+          "lines": true,
+          "linewidth": 1,
+          "links": [],
+          "nullPointMode": "null",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "span": 6,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "bond_status{host=\"$host\"}",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "{{ bond }}",
+              "refId": "A",
+              "step": 2
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "Bond status",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "decimals": 0,
+              "format": "none",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ]
+        }
+      ],
+      "repeat": null,
+      "repeatIteration": null,
+      "repeatRowId": null,
+      "showTitle": false,
+      "title": "Dashboard Row",
+      "titleSize": "h6"
+    },
+    {
+      "collapse": false,
+      "height": 447,
+      "panels": [
+        {
+          "columns": [],
+          "datasource": "prometheus",
+          "fontSize": "100%",
+          "hideTimeOverride": false,
+          "id": 4,
+          "links": [],
+          "pageSize": null,
+          "scroll": true,
+          "showHeader": true,
+          "sort": {
+            "col": 0,
+            "desc": true
+          },
+          "span": 12,
+          "styles": [
+            {
+              "alias": "Time",
+              "dateFormat": "YYYY-MM-DD HH:mm:ss",
+              "pattern": "Time",
+              "type": "date"
+            },
+            {
+              "alias": "",
+              "colorMode": null,
+              "colors": [
+                "rgba(245, 54, 54, 0.9)",
+                "rgba(237, 129, 40, 0.89)",
+                "rgba(50, 172, 45, 0.97)"
+              ],
+              "dateFormat": "YYYY-MM-DD HH:mm:ss",
+              "decimals": 2,
+              "pattern": "__name__",
+              "thresholds": [],
+              "type": "hidden",
+              "unit": "short"
+            },
+            {
+              "alias": "",
+              "colorMode": "row",
+              "colors": [
+                "rgba(245, 54, 54, 0.9)",
+                "rgba(26, 161, 12, 0.89)",
+                "rgba(50, 172, 45, 0.97)"
+              ],
+              "dateFormat": "YYYY-MM-DD HH:mm:ss",
+              "decimals": 0,
+              "pattern": "Value",
+              "thresholds": [
+                "1"
+              ],
+              "type": "number",
+              "unit": "none"
+            },
+            {
+              "alias": "",
+              "colorMode": null,
+              "colors": [
+                "rgba(245, 54, 54, 0.9)",
+                "rgba(237, 129, 40, 0.89)",
+                "rgba(50, 172, 45, 0.97)"
+              ],
+              "dateFormat": "YYYY-MM-DD HH:mm:ss",
+              "decimals": 2,
+              "pattern": "job",
+              "thresholds": [],
+              "type": "hidden",
+              "unit": "short"
+            },
+            {
+              "alias": "",
+              "colorMode": null,
+              "colors": [
+                "rgba(245, 54, 54, 0.9)",
+                "rgba(237, 129, 40, 0.89)",
+                "rgba(50, 172, 45, 0.97)"
+              ],
+              "decimals": 0,
+              "pattern": "/.*/",
+              "thresholds": [
+                ""
+              ],
+              "type": "number",
+              "unit": "none"
+            }
+          ],
+          "targets": [
+            {
+              "expr": "bond_slave_status{host=\"$host\"}",
+              "format": "table",
+              "intervalFactor": 2,
+              "legendFormat": "",
+              "refId": "A",
+              "step": 2
+            }
+          ],
+          "timeFrom": "1s",
+          "timeShift": null,
+          "title": "Bond slave status",
+          "transform": "table",
+          "transparent": false,
+          "type": "table"
+        }
+      ],
+      "repeat": null,
+      "repeatIteration": null,
+      "repeatRowId": null,
+      "showTitle": false,
+      "title": "Dashboard Row",
+      "titleSize": "h6"
+    }
+  ],
+  "schemaVersion": 14,
+  "style": "dark",
+  "tags": [],
+  "templating": {
+    "list": [
+      {
+        "allValue": null,
+        "current": {},
+        "datasource": "prometheus",
+        "hide": 0,
+        "includeAll": false,
+        "label": null,
+        "multi": false,
+        "name": "host",
+        "options": [],
+        "query": "label_values(bond_status,host)",
+        "refresh": 1,
+        "regex": "",
+        "sort": 1,
+        "tagValuesQuery": "",
+        "tags": [],
+        "tagsQuery": "",
+        "type": "query",
+        "useTags": false
+      }
+    ]
+  },
+  "time": {
+    "from": "now-5m",
+    "to": "now"
+  },
+  "timepicker": {
+    "refresh_intervals": [
+      "5s",
+      "10s",
+      "30s",
+      "1m",
+      "5m",
+      "15m",
+      "30m",
+      "1h",
+      "2h",
+      "1d"
+    ],
+    "time_options": [
+      "5m",
+      "15m",
+      "1h",
+      "6h",
+      "12h",
+      "24h",
+      "2d",
+      "7d",
+      "30d"
+    ]
+  },
+  "timezone": "",
+  "title": "Bond",
+  "version": 25
+}
+{% endraw %}
diff --git a/linux/files/grub_hugepages b/linux/files/grub_hugepages
index 509cc11..f7c7aa7 100644
--- a/linux/files/grub_hugepages
+++ b/linux/files/grub_hugepages
@@ -1,2 +1,2 @@
 {%- from "linux/map.jinja" import system with context %}
-GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT {%- for hugepages_type, hugepages in system.kernel.hugepages.iteritems() %}{%- if hugepages.get('default', False) %} default_hugepagesz={{ hugepages.size }} {%- endif %} hugepagesz={{ hugepages.size }} hugepages={{ hugepages.count }} {%- endfor %}"
+GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT {%- for hugepages_type, hugepages in system.kernel.hugepages.items() %}{%- if hugepages.get('default', False) %} default_hugepagesz={{ hugepages.size }} {%- endif %} hugepagesz={{ hugepages.size }} hugepages={{ hugepages.count }} {%- endfor %}"
diff --git a/linux/files/hosts b/linux/files/hosts
index 4971f6c..17ee079 100644
--- a/linux/files/hosts
+++ b/linux/files/hosts
@@ -31,7 +31,7 @@
         'ip6-allhosts'
     ],
 } -%}
-{%- for name, host in host_dict.iteritems() -%}
+{%- for name, host in host_dict.items() -%}
 {%- for hname in host.names -%}
 {%- if hname in hosts.get('127.0.1.1', []) -%}
 {%- do hosts.pop('127.0.1.1') -%}
diff --git a/linux/files/lvm.conf b/linux/files/lvm.conf
index 02f1c74..e6b8a58 100644
--- a/linux/files/lvm.conf
+++ b/linux/files/lvm.conf
@@ -129,7 +129,7 @@
         # Example
         # Accept every block device:
 
-        filter = [ {%- for vgname, vg in storage.lvm.iteritems() %}{%- if vg.get('enabled', True) %}{%- for dev in vg.devices %}"a|{{ dev }}*|"{%- if not loop.last %},{%- endif %}{%- endfor %}{%- endif %}{%- endfor %}, "r|.*|" ]
+        filter = [ {%- for vgname, vg in storage.lvm.items() %}{%- if vg.get('enabled', True) %}{%- for dev in vg.devices %}"a|{{ dev }}*|"{%- if not loop.last %},{%- endif %}{%- endfor %}{%- endif %}{%- endfor %}, "r|.*|" ]
 
         # filter = [ "a|.*/|" ]
         # Reject the cdrom drive:
diff --git a/linux/files/mcelog.conf b/linux/files/mcelog.conf
new file mode 100644
index 0000000..2b2302f
--- /dev/null
+++ b/linux/files/mcelog.conf
@@ -0,0 +1,199 @@
+{%- from "linux/map.jinja" import system with context %}
+{%- set mcelog = system.mcelog %}
+#
+# Example config file for mcelog
+# mcelog is the user space backend that decodes and process machine check events
+# (cpu hardware errors) reported by the CPU to the kernel
+#
+
+# general format
+#optionname = value
+# white space is not allowed in value currently, except at the end where it is dropped
+#
+
+# In general all command line options that are not commands work here.
+# See man mcelog or mcelog --help for a list.
+# e.g. to enable the --no-syslog option use
+#no-syslog = yes   (or no to disable)
+# when the option has a argument
+#logfile = /tmp/logfile
+# below are the options which are not command line options.
+
+# Set CPU type for which mcelog decodes events:
+#cpu = type
+# For valid values for type please see mcelog --help.
+# If this value is set incorrectly the decoded output will be likely incorrect.
+# By default when this parameter is not set mcelog uses the CPU it is running on
+# on very new kernels the mcelog events reported by the kernel also carry
+# the CPU type which is used too when available and not overriden.
+
+# Enable daemon mode:
+#daemon = yes
+# By default mcelog just processes the currently pending events and exits.
+# In daemon mode it will keep running as a daemon in the background and poll
+# the kernel for events and then decode them.
+
+# Filter out known broken events by default.
+filter = yes
+# Don't log memory errors individually.
+# They still get accounted if that is enabled.
+#filter-memory-errors = yes
+
+# output in undecoded raw format to be easier machine readable
+# (default is decoded).
+#raw = yes
+
+# Set CPU Mhz to decode uptime from time stamp counter (output
+# unreliable, not needed on new kernels which report the event time
+# directly. A lot of systems don't have a linear time stamp clock
+# and the output is wrong then.
+# Normally mcelog tries to figure out if it the TSC is reliable
+# and only uses the current frequency then.
+# Setting a frequency forces timestamp decoding.
+# This setting is obsolete with modern kernels which report the time
+# directly.
+#cpumhz = 1800.00
+
+# log output options
+# Log decoded machine checks in syslog (default stdout or syslog for daemon)
+#syslog = yes
+# Log decoded machine checks in syslog with error level
+#syslog-error = yes
+# Never log anything to syslog
+#no-syslog = yes
+# Append log output to logfile instead of stdout. Only when no syslog logging is active
+#logfile = filename
+
+{%- if mcelog.logging is defined %}
+
+{%- if mcelog.logging.syslog is defined %}
+syslog = {{ 'yes' if mcelog.logging.syslog else 'no' }}
+{%- endif %}
+{%- if mcelog.logging.syslog_error is defined %}
+syslog-error = {{ 'yes' if mcelog.logging.syslog_error else 'no' }}
+{%- endif %}
+{%- if mcelog.logging.no_syslog is defined %}
+no-syslog = {{ 'yes' if mcelog.logging.no_syslog else 'no' }}
+{%- endif %}
+{%- if mcelog.logging.logfile is defined %}
+logfile = {{ mcelog.logging.logfile }}
+{%- endif %}
+
+{%- endif %}
+# Use SMBIOS information to decode DIMMs (needs root).
+# This function is not recommended to use right now and generally not needed.
+# The exception is memdb prepopulation, which is configured separately below.
+#dmi = no
+
+# When in daemon mode run as this user after set up.
+# Note that the triggers will run as this user too.
+# Setting this to non root will mean that triggers cannot take some corrective
+# action, like offlining objects.
+#run-credentials-user = root
+
+# group to run as daemon with
+# default to the group of the run-credentials-user
+#run-credentials-group = nobody
+
+[server]
+# user allowed to access client socket.
+# when set to * match any
+# root is always allowed to access.
+# default: root only
+client-user = root
+# group allowed to access mcelog
+# When no group is configured any group matches (but still user checking).
+# when set to * match any
+#client-group = root
+# Path to the unix socket for client<->server communication.
+# When no socket-path is configured the server will not start
+#socket-path = /var/run/mcelog-client
+# When mcelog starts it checks if a server is already running. This configures the timeout
+# for this check.
+#initial-ping-timeout = 2
+#
+[dimm]
+# Is the in memory DIMM error tracking enabled?
+# Only works on systems with integrated memory controller and
+# which are supported.
+# Only takes effect in daemon mode.
+dimm-tracking-enabled = yes
+# Use DMI information from the BIOS to prepopulate DIMM database.
+# Note this might not work with all BIOS and requires mcelog to run as root.
+# Alternative is to let mcelog create DIMM objects on demand.
+dmi-prepopulate = yes
+#
+# Execute these triggers when the rate of corrected or uncorrected
+# Errors per DIMM exceeds the threshold.
+# Note when the hardware does not report DIMMs this might also
+# be per channel.
+# The default of 10/24h is reasonable for server quality
+# DDR3 DIMMs as of 2009/10.
+#uc-error-trigger = dimm-error-trigger
+uc-error-threshold = 1 / 24h
+#ce-error-trigger = dimm-error-trigger
+ce-error-threshold = 10 / 24h
+
+[socket]
+# Enable memory error accounting per socket.
+socket-tracking-enabled = yes
+
+# Threshold and trigger for uncorrected memory errors on a socket.
+# mem-uc-error-trigger = socket-memory-error-trigger
+
+mem-uc-error-threshold = 100 / 24h
+
+# Trigger script for corrected memory errors on a socket.
+mem-ce-error-trigger = socket-memory-error-trigger
+
+# Threshold on when to trigger a correct error for the socket.
+
+mem-ce-error-threshold = 100 / 24h
+
+#  Log socket error threshold explicitely?
+mem-ce-error-log = yes
+
+# Trigger script for uncorrected bus error events
+bus-uc-threshold-trigger = bus-error-trigger
+
+# Trigger script for uncorrected IOMCA erors
+iomca-threshold-trigger = iomca-error-trigger
+
+# Trigger script for other uncategorized errors
+unknown-threshold-trigger = unknown-error-trigger
+
+[cache]
+# Processing of cache error thresholds reported by Intel CPUs.
+cache-threshold-trigger = cache-error-trigger
+
+# Should cache threshold events be logged explicitely?
+cache-threshold-log = yes
+
+[page]
+# Memory error accouting per 4K memory page.
+# Threshold for the correct memory errors trigger script.
+memory-ce-threshold = 10 / 24h
+
+# Trigger script for corrected errors.
+# memory-ce-trigger = page-error-trigger
+
+# Should page threshold events be logged explicitely?
+memory-ce-log = yes
+
+# specify the internal action in mcelog to exceeding a page error threshold
+# this is done in addition to executing the trigger script if available
+# off      no action
+# account  only account errors
+# soft     try to soft-offline page without killing any processes
+#          This requires an uptodate kernel. Might not be successfull.
+# hard     try to hard-offline page by killing processes
+#          Requires an uptodate kernel. Might not be successfull.
+# soft-then-hard   First try to soft offline, then try hard offlining
+#memory-ce-action = off|account|soft|hard|soft-then-hard
+memory-ce-action = soft
+
+[trigger]
+# Maximum number of running triggers
+children-max = 2
+# execute triggers in this directory
+directory = /etc/mcelog
diff --git a/linux/files/mkhomedir b/linux/files/mkhomedir
new file mode 100644
index 0000000..b32caf6
--- /dev/null
+++ b/linux/files/mkhomedir
@@ -0,0 +1,7 @@
+{%- from "linux/map.jinja" import auth with context %}
+Name: Create home directory during login
+Default: yes
+Priority: 0
+Session-Type: Additional
+Session-Final:
+    required    pam_mkhomedir.so        skel=/etc/skel  umask={{ auth.mkhomedir.get('umask', '0022') }} silent
diff --git a/linux/files/modprobe.conf.jinja b/linux/files/modprobe.conf.jinja
index 2314bdb..8dcd782 100644
--- a/linux/files/modprobe.conf.jinja
+++ b/linux/files/modprobe.conf.jinja
@@ -2,8 +2,6 @@
 blacklist {{ module_name }}
 {%- else -%}
 
-{%- for option, value in module_content.get('option', {}) | dictsort -%}
-options {{ module_name }} {{ option }}={{ value }}
-{%- endfor %}
+options {{ module_name }}{% for option, value in module_content.get('option', {}) | dictsort %} {{ option }}={{ value }}{% endfor %}
 
 {%- endif %}
diff --git a/linux/files/motd.sh b/linux/files/motd.sh
index d29fc88..8d1871c 100644
--- a/linux/files/motd.sh
+++ b/linux/files/motd.sh
@@ -1,7 +1,7 @@
 {%- from "linux/map.jinja" import system with context -%}
 {%- for motd in system.motd -%}
 {%- if loop.index == index -%}
-{%- for name, value in motd.iteritems() -%}
+{%- for name, value in motd.items() -%}
 {%- if name == motd_name -%}{{ value }}{%- endif %}
 {%- endfor -%}
 {%- endif -%}
diff --git a/linux/files/multipath.conf b/linux/files/multipath.conf
index 382d775..b32f1b3 100644
--- a/linux/files/multipath.conf
+++ b/linux/files/multipath.conf
@@ -10,7 +10,7 @@
 
 blacklist {
         {%- for device in storage.multipath.get('blacklist_devices', []) %}
-        wwid                  {{ salt['cmd.run']('/lib/udev/scsi_id -g -u '+device) }}
+        wwid                  {{ salt['cmd.shell']('/lib/udev/scsi_id -g -u '+device) }}
         {%- endfor %}
         devnode "^(ram|raw|loop|fd|md|dm-|sr|scd|st|nbd)[0-9]*"
 }
diff --git a/linux/files/netconsole.conf b/linux/files/netconsole.conf
index 01b2ced..bf9ede3 100644
--- a/linux/files/netconsole.conf
+++ b/linux/files/netconsole.conf
@@ -8,7 +8,7 @@
 # unicast, could be multiline
 #netconsole ens3 192.168.1.32 fa:16:3e:8d:f6:d0
 {%- if system.netconsole is mapping and system.netconsole.target is mapping %}
-{%- for target, data in system.netconsole.target.iteritems() %}
+{%- for target, data in system.netconsole.target.items() %}
 {%- if data is mapping %}
 netconsole "{{ data.get('interface', '${interface}') }}" "{{ target }}" "{{ data.get('mac', '') }}"
 {%- endif %}
diff --git a/linux/files/nslcd.conf b/linux/files/nslcd.conf
new file mode 100644
index 0000000..4eb4134
--- /dev/null
+++ b/linux/files/nslcd.conf
@@ -0,0 +1,68 @@
+{%- from "linux/map.jinja" import ldap with context -%}
+# /etc/nslcd.conf
+# nslcd configuration file. See nslcd.conf(5)
+# for details.
+
+# The user and group nslcd should run as.
+uid {{ ldap.uid }}
+gid {{ ldap.gid }}
+
+{%- if ldap.enabled %}
+
+{%- if ldap.uri is defined %}
+# The location at which the LDAP server(s) should be reachable.
+uri {{ ldap.uri }}
+{%- endif %}
+
+{%- if ldap.base is defined %}
+# The search base that will be used for all queries.
+base {{ ldap.base }}
+{%- endif %}
+
+# The LDAP protocol version to use.
+ldap_version {{ ldap.version }}
+
+{%- if ldap.binddn is defined %}
+# The DN to bind with for normal lookups.
+binddn {{ ldap.binddn }}
+{%- if ldap.bindpw is defined %}
+bindpw {{ ldap.bindpw }}
+{%- endif %}
+{%- endif %}
+
+{%- if ldap.rootpwmoddn is defined %}
+# The DN used for password modifications by root.
+rootpwmoddn {{ ldap.rootpwmoddn }}
+{%- endif %}
+
+# SSL options
+#ssl off
+#tls_reqcert never
+#tls_cacertfile /etc/ssl/certs/ca-certificates.crt
+
+# The search scope.
+scope {{ ldap.scope }}
+
+{%- if ldap.pagesize is defined %}
+pagesize {{ ldap.pagesize }}
+{%- endif %}
+{%- if ldap.referrals is defined %}
+referrals {{ ldap.referrals }}
+{%- endif %}
+
+{%- if ldap.filter is defined %}
+# Filters
+{%- for key, value in ldap.filter.items() %}
+filter {{ key }} {{ value }}
+{%- endfor %}
+{%- endif %}
+{%- if ldap.map is defined %}
+# Mappings
+{%- for map_name,map in ldap.map.items() %}
+{%- for key, value in map.items() %}
+map {{ map_name }} {{ key }} {{ value }}
+{%- endfor %}
+{%- endfor %}
+{%- endif %}
+
+{%- endif %}
diff --git a/linux/files/nsswitch.conf b/linux/files/nsswitch.conf
new file mode 100644
index 0000000..74f6332
--- /dev/null
+++ b/linux/files/nsswitch.conf
@@ -0,0 +1,21 @@
+{%- from "linux/map.jinja" import ldap with context -%}
+# /etc/nsswitch.conf
+#
+# Example configuration of GNU Name Service Switch functionality.
+# If you have the `glibc-doc-reference' and `info' packages installed, try:
+# `info libc "Name Service Switch"' for information about this file.
+
+passwd:         compat{%- if ldap.enabled %} ldap{%- endif %}
+group:          compat{%- if ldap.enabled %} ldap{%- endif %}
+shadow:         compat{%- if ldap.enabled %} ldap{%- endif %}
+gshadow:        files
+
+hosts:          files dns
+networks:       files
+
+protocols:      db files
+services:       db files
+ethers:         db files
+rpc:            db files
+
+netgroup:       nis
diff --git a/linux/files/openvswitch-switch.default b/linux/files/openvswitch-switch.default
new file mode 100644
index 0000000..06c769d
--- /dev/null
+++ b/linux/files/openvswitch-switch.default
@@ -0,0 +1,44 @@
+{%- from "linux/map.jinja" import network with context %}
+{%- set openvswitch = network.openvswitch %}
+# This is a POSIX shell fragment                -*- sh -*-
+
+# FORCE_COREFILES: If 'yes' then core files will be enabled.
+# FORCE_COREFILES=yes
+
+# OVS_CTL_OPTS: Extra options to pass to ovs-ctl.  This is, for example,
+# a suitable place to specify --ovs-vswitchd-wrapper=valgrind.
+# OVS_CTL_OPTS=
+
+# OVS_VSWITCHD_OPTS: Extra options to pass to ovs-ctl.
+# Options to start Open vSwitch daemon with.
+# Example: '-vconsole:dbg -vsyslog:dbg -vfile:dbg -vFACILITY:clock2'
+# OVS_VSWITCHD_OPTS=
+{%- if openvswitch.get('logging', {}).vswitchd is defined %}
+  {%- set _vswitchd_opts = [] %}
+  {%- for opt in ['console', 'file', 'syslog'] %}
+    {%- if openvswitch.logging.vswitchd.get(opt)  %}
+      {%- do _vswitchd_opts.append("-v"+ opt + ":" + openvswitch.logging.vswitchd.get(opt)) %}
+    {%- endif %}
+  {%- endfor %}
+  {%- if openvswitch.logging.vswitchd.facility is defined  %}
+    {%- do _vswitchd_opts.append("-vFACILITY:" + openvswitch.logging.vswitchd.facility) %}
+  {%- endif %}
+OVS_VSWITCHD_OPTS="{{ ' '.join(_vswitchd_opts) }}"
+{%- endif %}
+
+# OVSDB_OPTS: Extra options to pass to ovs-ctl.
+# Options to start Open vSwitch DB daemon with.
+# Example: '-vconsole:dbg -vsyslog:dbg -vfile:dbg -vFACILITY:clock2'
+# OVSDB_OPTS=
+{%- if openvswitch.get('logging', {}).ovsdb is defined %}
+  {%- set _ovsdb_opts = [] %}
+  {%- for opt in ['console', 'file', 'syslog'] %}
+    {%- if openvswitch.logging.ovsdb.get(opt)  %}
+      {%- do _ovsdb_opts.append("-v" + opt + ":" + openvswitch.logging.ovsdb.get(opt)) %}
+    {%- endif %}
+  {%- endfor %}
+  {%- if openvswitch.logging.ovsdb.facility is defined  %}
+    {%- do _ovsdb_opts.append("-vFACILITY:" + openvswitch.logging.ovsdb.facility) %}
+  {%- endif %}
+OVSDB_OPTS="{{ ' '.join(_ovsdb_opts) }}"
+{%- endif %}
diff --git a/linux/files/pam-add-profile b/linux/files/pam-add-profile
new file mode 100644
index 0000000..4f1a5fd
--- /dev/null
+++ b/linux/files/pam-add-profile
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+if [ "$(basename $EDITOR 2>/dev/null)" == "$(basename $0)" ]; then
+    PROFILES=$(debconf-get-selections | grep libpam-runtime/profiles | awk '{$1=$2=$3=""; print $0}')
+
+    for profile in ${PROFILE[@]}; do
+        if [[ $PROFILES =~ $profile ]]; then
+            continue
+        else
+            PROFILES="${PROFILES}, ${profile}"
+        fi
+    done
+
+    for profile in /usr/share/pam-configs/*; do
+        profile_name=$(grep Name: $profile | cut -d ' ' -f 2-)
+        PROFILES=$(echo $PROFILES | sed s,$(basename $profile),"${profile_name}",g)
+    done
+
+    cat > $1 <<EOF
+libpam-runtime/profiles="${PROFILES}"
+EOF
+else
+    [ -z $1 ] && { echo "Usage: $0 [PROFILE]"; exit 1; }
+    export PROFILE="$*"
+    EDITOR=/usr/local/bin/pam-add-profile DEBIAN_FRONTEND=editor pam-auth-update --force
+fi
diff --git a/linux/files/prompt.sh b/linux/files/prompt.sh
index 0663bfd..27061d1 100644
--- a/linux/files/prompt.sh
+++ b/linux/files/prompt.sh
@@ -6,7 +6,7 @@
 # Don't set prompt on non-interactive shell
 [[ $- == *i* ]] || return 0
 
-{%- for user, prompt in system.prompt.iteritems() %}
+{%- for user, prompt in system.prompt.items() %}
 {% if user != "default" %}
 if [ "$USERNAME" == "{{ user }}" ]; then
   export PS1="{{ prompt }} "
diff --git a/linux/files/setup-loopback-device.upstart b/linux/files/setup-loopback-device.upstart
index a1acd24..2b63ac7 100644
--- a/linux/files/setup-loopback-device.upstart
+++ b/linux/files/setup-loopback-device.upstart
@@ -1,12 +1,10 @@
 description "Setup {{ device_name }} device"
 
 start on filesystem
-task
 
-pre-start script
-  if /sbin/losetup {{ device_name }}; then
-    stop ; exit 0
-  fi
+pre-start exec losetup {{ device_name }} {{ file }}
+post-stop exec losetup -d {{ device_name }}
+
+script
+  while losetup {{ device_name }} ; do sleep 60 ; done
 end script
-
-exec losetup {{ device_name }} {{ file }}
diff --git a/linux/files/sources.list b/linux/files/sources.list
index ed41e7b..3166ba6 100644
--- a/linux/files/sources.list
+++ b/linux/files/sources.list
@@ -1,4 +1,4 @@
-{%- for name, repo in default_repos.iteritems() %}
+{%- for name, repo in default_repos.items() %}
 # Repository {{ name }}
 {{ repo.source }}
 {%- endfor %}
diff --git a/linux/files/sudoer-aliases b/linux/files/sudoer-aliases
index 9e44886..38b434c 100644
--- a/linux/files/sudoer-aliases
+++ b/linux/files/sudoer-aliases
@@ -1,19 +1,19 @@
 # sudoer aliases, file managed by salt-minion
 # DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
 
-{%- for alias,commands in aliases.get('command',{}).iteritems() %}
+{%- for alias,commands in aliases.get('command',{}).items() %}
 Cmnd_Alias {{ alias }}={{ commands|join(', ') }}
 {%- endfor %}
 
-{%- for alias,users in aliases.get('user',{}).iteritems() %}
+{%- for alias,users in aliases.get('user',{}).items() %}
 User_Alias {{ alias }}={{ users|join(', ') }}
 {%- endfor %}
 
-{%- for alias,users in aliases.get('runas',{}).iteritems() %}
+{%- for alias,users in aliases.get('runas',{}).items() %}
 Runas_Alias {{ alias }}={{ users|join(', ') }}
 {%- endfor %}
 
-{%- for alias,hosts in aliases.get('host',{}).iteritems() %}
+{%- for alias,hosts in aliases.get('host',{}).items() %}
 Host_Alias {{ alias }}={{ hosts|join(', ') }}
 {%- endfor %}
 
diff --git a/linux/files/sudoer-groups b/linux/files/sudoer-groups
index b0c1fdf..ed8c479 100644
--- a/linux/files/sudoer-groups
+++ b/linux/files/sudoer-groups
@@ -1,7 +1,7 @@
 # sudoer groups, file managed by salt-minion
 # DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
 
-{%- for group,spec in groups.iteritems() %}
+{%- for group,spec in groups.items() %}
 %{{ spec.name|default(group) }} {{ spec.get('hosts', ['ALL'])|join(',') }}=({{ spec.get('runas', ['ALL'])|join(', ') }}) {% if spec.get('nopasswd', True) %}NOPASSWD: {% endif %}{{ spec.get('commands', ['ALL'])|join(', ') }}
 {%- endfor %}
 
diff --git a/linux/files/sudoer-users b/linux/files/sudoer-users
index 738d25d..13cb2fe 100644
--- a/linux/files/sudoer-users
+++ b/linux/files/sudoer-users
@@ -1,7 +1,7 @@
 # sudoer users, file managed by salt-minion
 # DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
 
-{%- for user,spec in users.iteritems() %}
+{%- for user,spec in users.items() %}
 {{ spec.name|default(user) }} {{ spec.get('hosts', ['ALL'])|join(',') }}=({{ spec.get('runas', ['ALL'])|join(', ') }}) {% if spec.get('nopasswd', True) %}NOPASSWD:{% endif %}{% if spec.get('setenv', False) %}SETENV:{% endif %} {{ spec.get('commands', ['ALL'])|join(', ') }}
 {%- endfor %}
 
diff --git a/linux/files/sysfs.conf b/linux/files/sysfs.conf
index 513a53a..ff26672 100644
--- a/linux/files/sysfs.conf
+++ b/linux/files/sysfs.conf
@@ -1,9 +1,9 @@
 # Sysfs file for {{ name }} managed by salt-minion(1)
 #   DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
 
-{%- for key, value in sysfs.iteritems() %}
+{%- for key, value in sysfs.items() %}
   {%- if key in ["mode", "owner"] %}
-    {%- for attr, val in value.iteritems() %}
+    {%- for attr, val in value.items() %}
 mode {{ attr }} = {{ val }}
     {%- endfor %}
   {%- else %}
diff --git a/linux/files/systemd-network.conf b/linux/files/systemd-network.conf
index 6a21b4f..4777adf 100644
--- a/linux/files/systemd-network.conf
+++ b/linux/files/systemd-network.conf
@@ -1,8 +1,8 @@
 {%- from "linux/map.jinja" import system with context -%}
-{%- for section, options in settings.iteritems() %}
+{%- for section, options in settings.items() %}
 
 [{{ section[0].upper() + section[1:] }}]
-{%- for option, value in options.iteritems() %}
+{%- for option, value in options.items() %}
 {{ option[0].upper() + option[1:] }}={{ value }}
 {%- endfor %}
 {%- endfor %}
diff --git a/linux/files/systemd.conf b/linux/files/systemd.conf
index 2663aed..3edb9a0 100644
--- a/linux/files/systemd.conf
+++ b/linux/files/systemd.conf
@@ -1,7 +1,7 @@
 {%- from "linux/map.jinja" import system with context -%}
-{%- for section, options in settings.iteritems() %}
+{%- for section, options in settings.items() %}
 [{{ section }}]
-{%- for option, value in options.iteritems() %}
+{%- for option, value in options.items() %}
 {{ option }}={{ value }}
 {%- endfor -%}
 {%- endfor -%}
diff --git a/linux/files/telegraf.conf b/linux/files/telegraf.conf
new file mode 100644
index 0000000..6a45b6b
--- /dev/null
+++ b/linux/files/telegraf.conf
@@ -0,0 +1,9 @@
+[[inputs.bond]]
+{%- include 'telegraf/files/input/_common.conf' %}
+{%- if values.bond_interfaces is defined %}
+  bond_interfaces = {{ values.bond_interfaces | json }}
+{%- endif %}
+{%- if values.host_proc is defined %}
+  host_proc = "{{ values.host_proc | json }}"
+{%- endif %}
+{%- include 'telegraf/files/input/_filters.conf' %}
diff --git a/linux/map.jinja b/linux/map.jinja
index 29dba83..4236a14 100644
--- a/linux/map.jinja
+++ b/linux/map.jinja
@@ -85,6 +85,43 @@
     },
 }, grain='os_family', merge=salt['pillar.get']('linux:system')) %}
 
+{% set banner = salt['grains.filter_by']({
+    'BaseDefaults': {
+        'enabled': false,
+    },
+}, grain='os_family', merge=salt['pillar.get']('linux:system:banner'), base='BaseDefaults') %}
+
+{% set auth = salt['grains.filter_by']({
+    'Arch': {
+        'enabled': false,
+    },
+    'RedHat': {
+        'enabled': false,
+    },
+    'Debian': {
+        'enabled': false,
+    },
+}, grain='os_family', merge=salt['pillar.get']('linux:system:auth')) %}
+
+{% set ldap = salt['grains.filter_by']({
+    'RedHat': {
+        'enabled': false,
+        'pkgs': ['openldap-clients', 'nss-pam-ldapd', 'authconfig'],
+        'version': '3',
+        'scope': 'sub',
+        'uid': 'nslcd',
+        'gid': 'nslcd',
+    },
+    'Debian': {
+        'enabled': false,
+        'pkgs': ['libnss-ldapd', 'libpam-ldapd'],
+        'version': '3',
+        'scope': 'sub',
+        'uid': 'nslcd',
+        'gid': 'nslcd',
+    },
+}, grain='os_family', merge=salt['pillar.get']('linux:system:auth:ldap')) %}
+
 {#    'network_name', #}
 
 {% set interface_params = [
@@ -110,6 +147,13 @@
     'maxwait',
     'stp',
     'gro',
+    'rx',
+    'tx',
+    'sg',
+    'tso',
+    'ufo',
+    'gso',
+    'lro',
     'lacp_rate',
     'ad_select',
     'downdelay',
@@ -121,7 +165,7 @@
     'arp_ip_target',
     'primary',
 ] %}
-
+{% set debian_headers = "linux-headers-" + grains.get('kernelrelease')|string %}
 {% set network = salt['grains.filter_by']({
     'Arch': {
         'pkgs': ['wpa_supplicant', 'dhclient', 'wireless_tools', 'ifenslave'],
@@ -139,13 +183,14 @@
         'host': {},
         'mine_dns_records': False,
         'dhclient_config': '/etc/dhcp/dhclient.conf',
+        'ovs_nowait': False,
     },
     'Debian': {
         'pkgs': ['ifenslave'],
         'hostname_file': '/etc/hostname',
         'bridge_pkgs': ['bridge-utils'],
         'ovs_pkgs': ['openvswitch-switch', 'bridge-utils'],
-        'dpdk_pkgs': ['dpdk', 'dpdk-dev', 'dpdk-dkms', 'dpdk-igb-uio-dkms', 'dpdk-rte-kni-dkms'],
+        'dpdk_pkgs': ['dpdk', 'dpdk-dev', 'dpdk-igb-uio-dkms', 'dpdk-rte-kni-dkms', debian_headers.encode('utf8') ],
         'network_manager': False,
         'systemd': {},
         'interface': {},
@@ -157,6 +202,7 @@
         'host': {},
         'mine_dns_records': False,
         'dhclient_config': '/etc/dhcp/dhclient.conf',
+        'ovs_nowait': False,
     },
     'RedHat': {
         'pkgs': ['iputils'],
@@ -174,6 +220,7 @@
         'host': {},
         'mine_dns_records': False,
         'dhclient_config': '/etc/dhcp/dhclient.conf',
+        'ovs_nowait': False,
     },
 }, grain='os_family', merge=salt['pillar.get']('linux:network')) %}
 
@@ -227,8 +274,11 @@
              'service': 'multipath'
          },
     },
-}, grain='os_family', merge=salt['pillar.get']('linux:storage')) %}
-
+}, merge=salt['grains.filter_by']({
+    'trusty': {
+        'lvm_services': ['udev'],
+    },
+}, grain='oscodename', merge=salt['pillar.get']('linux:storage'))) %}
 
 {% set monitoring = salt['grains.filter_by']({
     'default': {
diff --git a/linux/meta/fluentd.yml b/linux/meta/fluentd.yml
index 6b4041c..94b1fbc 100644
--- a/linux/meta/fluentd.yml
+++ b/linux/meta/fluentd.yml
@@ -2,9 +2,52 @@
 agent:
   plugin:
     fluent-plugin-systemd:
-      gem: ['fluent-plugin-systemd']
+      deb: ['td-agent-additional-plugins']
   config:
     label:
+      default_metric:
+        filter:
+          metric_failed_user:
+            tag: metric.failed_user
+            type: prometheus
+            metric:
+              - name: failed_logins_total
+                type: counter
+                desc: The total number of failed logins.
+            label:
+              - name: host
+                value: ${Hostname}
+          metric_out_of_memory:
+            tag: metric.out_of_memory
+            type: prometheus
+            metric:
+              - name: out_of_memory_total
+                type: counter
+                desc: The total number of OOM.
+            label:
+              - name: host
+                value: ${Hostname}
+          metric_hdd_errors_parse:
+            tag: metric.hdd_errors
+            type: parser
+            key_name: Payload
+            parser:
+              type: regexp
+              format: '/(?<device>[sv]d[a-z]+\d*)/'
+          metric_hdd_errors:
+            tag: metric.hdd_errors
+            require:
+              - metric_hdd_errors_parse
+            type: prometheus
+            metric:
+              - name: hdd_errors_total
+                type: counter
+                desc: The total number of hdd errors.
+            label:
+              - name: host
+                value: ${Hostname}
+              - name: device
+                value: ${device}
       systemd:
         input:
           systemd:
@@ -37,11 +80,93 @@
             tag: systemd.source
             type: rewrite_tag_filter
             rule:
-              - name: service
-                regexp: '^(.*)\.(.*)$'
+              - name: ident
+                regexp: '^(.*)$'
                 result: __TAG__.$1
           push_to_default:
             tag: 'systemd.source.*'
+            type: copy
+            store:
+              - type: relabel
+                label: default_output
+              - type: rewrite_tag_filter
+                rule:
+                  - name: Payload
+                    regexp: '^Invalid user'
+                    result: metric.failed_user
+                  - name: Payload
+                    regexp: '^Out of memory'
+                    result: metric.out_of_memory
+                  - name: Payload
+                    regexp: >-
+                      'error.*\b[sv]d[a-z]{1,2}\d{0,3}\b.*'
+                    result: metric.hdd_errors
+                  - name: Payload
+                    regexp: >-
+                      '\b[sv]d[a-z]{1,2}\d{0,3}\b.*error'
+                    result: metric.hdd_errors
+          push_to_metric:
+            tag: 'metric.**'
             type: relabel
-            label: default_output
+            label: default_metric
+{%- else %}
+agent:
+  config:
+    label:
+      default_metric:
+        filter:
+          metric_hdd_errors_parse:
+            tag: metric.hdd_errors
+            type: parser
+            key_name: Payload
+            parser:
+              type: regexp
+              format: '/(?<device>[sv]d[a-z]+\d*)/'
+          metric_hdd_errors:
+            tag: metric.hdd_errors
+            require:
+              - metric_hdd_errors_parse
+            type: prometheus
+            metric:
+              - name: hdd_errors_total
+                type: counter
+                desc: The total number of hdd errors.
+            label:
+              - name: host
+                value: ${Hostname}
+              - name: device
+                value: ${device}
+      syslog:
+        input:
+          syslog_file:
+            type: tail
+            tag: linux.syslog
+            path: /var/log/syslog
+            pos_file: {{ pillar.fluentd.agent.dir.positiondb }}/linux_syslog.pos
+            suppress_parse_error_log: true
+            parser:
+              type: regexp
+              format: >-
+                '/(?<Payload>.*(?<device>[sv]d[a-z]{1,2}\d{0,3}).*)/'
+        match:
+          push_to_default:
+            tag: 'linux.**'
+            type: copy
+            store:
+              - type: relabel
+                label: default_output
+              - type: rewrite_tag_filter
+                rule:
+                  - name: Payload
+                    regexp: >-
+                      'error.*\b[sv]d[a-z]{1,2}\d{0,3}\b.*'
+                    result: metric.hdd_errors
+                  - name: Payload
+                    regexp: >-
+                      '\b[sv]d[a-z]{1,2}\d{0,3}\b.*error'
+                    result: metric.hdd_errors
+          push_to_metric:
+            tag: 'metric.**'
+            type: relabel
+            label: default_metric
 {%- endif %}
diff --git a/linux/meta/grafana.yml b/linux/meta/grafana.yml
index 06efc68..6661c7f 100644
--- a/linux/meta/grafana.yml
+++ b/linux/meta/grafana.yml
@@ -1,3 +1,4 @@
+{%- from "linux/map.jinja" import monitoring with context %}
 dashboard:
   linux_prometheus:
     datasource: prometheus
@@ -7,3 +8,9 @@
     datasource: influxdb
     format: json
     template: linux/files/grafana_dashboards/system_influxdb.json
+{%- if monitoring.bond_status.interfaces is defined and monitoring.bond_status.interfaces %}
+  linux_bond:
+    datasource: prometheus
+    format: json
+    template: linux/files/grafana_dashboards/bond_prometheus.json
+{%- endif %}
diff --git a/linux/meta/meta.yml b/linux/meta/meta.yml
index 8d17c54..c2dfa95 100644
--- a/linux/meta/meta.yml
+++ b/linux/meta/meta.yml
@@ -6,7 +6,7 @@
   type: software-system
   relations:
   {%- if system.repo is defined %}
-  {%- for repo_name, repo in system.repo.iteritems() %}
+  {%- for repo_name, repo in system.repo.items() %}
   {%- if repo.get('enabled', True) %}
   - service: apt.repo
     host_external: {{ repo.source }}
diff --git a/linux/meta/prometheus.yml b/linux/meta/prometheus.yml
index e8a26e3..5d75f74 100644
--- a/linux/meta/prometheus.yml
+++ b/linux/meta/prometheus.yml
@@ -32,6 +32,16 @@
         summary: 'Free open files for {{ $labels.path }} too low on {{ $labels.host }}'
         description: 'Host {{ $labels.host }}) will run out of free open files in less than 8 hours.'
       {% endraw %}
+    SystemDiskErrors:
+      if: 'increase(hdd_errors_total[5m]) > 0'
+      {% raw %}
+      labels:
+        severity: critical
+        service: system
+      annotations:
+        summary: 'Disk {{ $labels.device }} is failing'
+        description: 'The disk ({{ $labels.device }}) is reporting errors on {{ $labels.host }}.'
+      {% endraw %}
     SystemDiskSpaceFull:
       if: 'disk_used_percent >= 99 and disk_inodes_total > 0'
       {% raw %}
@@ -132,3 +142,35 @@
       annotations:
         summary: 'Swap output throughput too high on {{ $labels.host }}'
         description: 'The rate of swap output bytes is too high on node {{ $labels.host }} (current value={{ $value }}b/s, threshold={% endraw %}{{ swap_out_threshold }}b/s).'
+{%- if monitoring.bond_status.interfaces is defined and monitoring.bond_status.interfaces %}
+    BondInterfaceDown:
+      if: 'bond_status < 1'
+      {% raw %}
+      labels:
+        severity: critical
+        service: system
+      annotations:
+        summary: 'Bond status interface {{ $labels.bond }} is DOWN on {{ $labels.host }}'
+        description: 'The bond interface ({{ $labels.bond }) has all ifaces in a down state on {{ $labels.host }}.'
+      {% endraw %}
+    BondSlaveInterfacesMinimum:
+      if: '(sum(bond_slave_status) BY (bond,host)) / (count(bond_slave_status) BY (bond,host)) <= 0.5'
+      {% raw %}
+      labels:
+        severity: critical
+        service: system
+      annotations:
+        summary: 'At least half of Bond slave interfaces {{ $labels.bond }} are DOWN on {{ $labels.host }}'
+        description: 'The bond interface ({{ $labels.bond }) has at least half of slave ifaces in a down state on {{ $labels.host }}.'
+      {% endraw %}
+    BondSlaveInterfaceStatus:
+      if: 'bond_slave_status < 1'
+      {% raw %}
+      labels:
+        severity: warning
+        service: system
+      annotations:
+        summary: 'Bond slave interface {{ $labels.interface }} is DOWN on {{ $labels.host }} for {{ $labels.bond }}'
+        description: 'The bond slave interface ({{ $labels.interface }) is in DOWN state for {{ $labels.bond }} on {{ $labels.host }}.'
+      {% endraw %}
+{%- endif %}
diff --git a/linux/meta/telegraf.yml b/linux/meta/telegraf.yml
index 45fef5c..854bf26 100644
--- a/linux/meta/telegraf.yml
+++ b/linux/meta/telegraf.yml
@@ -1,3 +1,4 @@
+{%- from "linux/map.jinja" import monitoring with context %}
 agent:
   input:
     cpu:
@@ -23,3 +24,13 @@
     swap:
     system:
     linux_sysctl_fs:
+{%- if monitoring.bond_status.interfaces is defined and monitoring.bond_status.interfaces %}
+    bond:
+      template: linux/files/telegraf.conf
+{%- if monitoring.bond_status.interfaces is list %}
+      bond_interfaces: {{ monitoring.bond_status.interfaces }}
+{%- endif %}
+{%- if monitoring.bond_status.host_proc is defined %}
+      host_proc: {{ monitoring.bond_status.host_proc }}
+{%- endif %}
+{%- endif %}
diff --git a/linux/network/dpdk.sls b/linux/network/dpdk.sls
index 1ac9e25..e16f9c9 100644
--- a/linux/network/dpdk.sls
+++ b/linux/network/dpdk.sls
@@ -9,6 +9,7 @@
 linux_dpdk_kernel_module:
   kmod.present:
   - name: {{ network.dpdk.driver }}
+  - persist: true
   - require:
     - pkg: linux_dpdk_pkgs
   - require_in:
@@ -63,7 +64,7 @@
 
 linux_network_dpdk_ovs_option_{{ option }}:
   cmd.run:
-  - name: 'ovs-vsctl set Open_vSwitch . other_config:{{ option }}'
+  - name: 'ovs-vsctl{%- if network.ovs_nowait %} --no-wait{%- endif %} set Open_vSwitch . other_config:{{ option }}'
   - watch_in:
     - service: service_openvswitch
   - require:
@@ -73,6 +74,15 @@
 
 {%- endfor %}
 
+openvswitch_dpdk_ovs_alternative:
+  alternatives.remove:
+  - name: ovs-vswitchd
+  - path: /usr/lib/openvswitch-switch/ovs-vswitchd
+  - require:
+    - pkg: openvswitch_dpdk_pkgs
+  - watch_in:
+    - service: service_openvswitch
+
 service_openvswitch:
   service.running:
   - name: openvswitch-switch
@@ -82,26 +92,26 @@
 
 {%- endif %}
 
-{%- for interface_name, interface in network.interface.iteritems() if interface.get('enabled', True) %}
+{%- for interface_name, interface in network.interface.items() if interface.get('enabled', True) %}
 
   {%- if interface.type == "dpdk_ovs_bond" %}
 
     {%- set bond_interfaces = {} %}
-    {%- for iface_name, iface in network.interface.iteritems() if iface.get('enabled', True) and iface.get('bond',"") == interface_name %}
+    {%- for iface_name, iface in network.interface.items() if iface.get('enabled', True) and iface.get('bond',"") == interface_name %}
       {#- Get list of child interfaces #}
       {%- do bond_interfaces.update({iface_name: iface}) %}
     {%- endfor %}
 
 linux_network_dpdk_bond_interface_{{ interface_name }}:
   cmd.run:
-    - name: "ovs-vsctl add-bond {{ interface.bridge }} {{ interface_name }} {{ bond_interfaces.keys()|join(' ') }} {% for iface_name, iface in bond_interfaces.iteritems() %}-- set Interface {{ iface_name }} type=dpdk options:dpdk-devargs={{ iface.pci }} {% endfor %}"
+    - name: "ovs-vsctl{%- if network.ovs_nowait %} --no-wait{%- endif %} add-bond {{ interface.bridge }} {{ interface_name }} {{ bond_interfaces.keys()|join(' ') }} {% for iface_name, iface in bond_interfaces.items() %}-- set Interface {{ iface_name }} type=dpdk options:dpdk-devargs={{ iface.pci }} {% endfor %}"
     - unless: "ovs-vsctl show | grep {{ interface_name }}"
     - require:
         - cmd: linux_network_dpdk_bridge_interface_{{ interface.bridge }}
 
 linux_network_dpdk_bond_mode_{{ interface_name }}:
   cmd.run:
-    - name: "ovs-vsctl set port {{ interface_name }} bond_mode={{ interface.mode }}"
+    - name: "ovs-vsctl{%- if network.ovs_nowait %} --no-wait{%- endif %} set port {{ interface_name }} bond_mode={{ interface.mode }}{%- if interface.mode == 'balance-slb' %} lacp=active{%- endif %}"
     - unless: "ovs-appctl bond/show {{ interface_name }} | grep {{ interface.mode }}"
     - require:
         - cmd: linux_network_dpdk_bond_interface_{{ interface_name }}
@@ -110,7 +120,7 @@
 
 linux_network_dpdk_bridge_interface_{{ interface_name }}:
   cmd.run:
-    - name: "ovs-vsctl add-br {{ interface_name }} -- set bridge {{ interface_name }} datapath_type=netdev"
+    - name: "ovs-vsctl{%- if network.ovs_nowait %} --no-wait{%- endif %} add-br {{ interface_name }} -- set bridge {{ interface_name }} datapath_type=netdev{% if interface.tag is defined %} -- set port {{ interface_name }} tag={{ interface.tag }}{% endif %}"
     - unless: "ovs-vsctl show | grep {{ interface_name }}"
 
     {# OVS dpdk needs ip address for vxlan termination on bridge br-prv #}
@@ -147,7 +157,7 @@
 
 linux_network_dpdk_bridge_port_interface_{{ interface_name }}:
   cmd.run:
-    - name: "ovs-vsctl add-port {{ interface.bridge }} dpdk0 -- set Interface dpdk0 type=dpdk options:dpdk-devargs={{ interface.pci }}"
+    - name: "ovs-vsctl{%- if network.ovs_nowait %} --no-wait{%- endif %} add-port {{ interface.bridge }} dpdk0 -- set Interface dpdk0 type=dpdk options:dpdk-devargs={{ interface.pci }}"
     - unless: "ovs-vsctl show | grep dpdk0"
     - require:
       - cmd: linux_network_dpdk_bridge_interface_{{ interface.bridge }}
@@ -161,7 +171,7 @@
 
 linux_network_dpdk_bridge_port_interface_n_rxq_{{ interface_name }}:
   cmd.run:
-    - name: "ovs-vsctl set Interface {{ interface_name }} options:n_rxq={{ interface.n_rxq }} "
+    - name: "ovs-vsctl{%- if network.ovs_nowait %} --no-wait{%- endif %} set Interface {{ interface_name }} options:n_rxq={{ interface.n_rxq }} "
     - unless: |
         ovs-vsctl get Interface {{ interface_name }} options | grep 'n_rxq="{{ interface.n_rxq }}"'
 
@@ -171,7 +181,7 @@
 
 linux_network_dpdk_bridge_port_interface_pmd_rxq_affinity_{{ interface_name }}:
   cmd.run:
-    - name: "ovs-vsctl set Interface {{ interface_name }} other_config:pmd-rxq-affinity={{ interface.pmd_rxq_affinity }} "
+    - name: "ovs-vsctl{%- if network.ovs_nowait %} --no-wait{%- endif %} set Interface {{ interface_name }} other_config:pmd-rxq-affinity={{ interface.pmd_rxq_affinity }} "
     - unless: |
         ovs-vsctl get Interface {{ interface_name }} other_config | grep 'pmd-rxq-affinity="{{ interface.pmd_rxq_affinity }}"'
 
@@ -182,7 +192,7 @@
 {# MTU ovs dpdk setup on interfaces #}
 linux_network_dpdk_bridge_port_interface_mtu_{{ interface_name }}:
   cmd.run:
-    - name: "ovs-vsctl set Interface {{ interface_name }} mtu_request={{ interface.mtu }} "
+    - name: "ovs-vsctl{%- if network.ovs_nowait %} --no-wait{%- endif %} set Interface {{ interface_name }} mtu_request={{ interface.mtu }} "
     - unless: "ovs-vsctl get Interface {{ interface_name }} mtu_request | grep {{ interface.mtu }}"
 
   {%- endif %}
diff --git a/linux/network/host.sls b/linux/network/host.sls
index 14bd837..1281f73 100644
--- a/linux/network/host.sls
+++ b/linux/network/host.sls
@@ -5,7 +5,7 @@
 
 {%- if network.mine_dns_records %}
 
-{%- for node_name, node_grains in salt['mine.get']('*', 'grains.items').iteritems() %}
+{%- for node_name, node_grains in salt['mine.get']('*', 'grains.items').items() %}
 {%- if node_grains.get('dns_records', []) is iterable %}
 {%- for record in node_grains.get('dns_records', []) %}
 {%- set record_key = node_name ~ '-' ~ loop.index %}
@@ -28,12 +28,12 @@
 
 {%- else %}
 
-{%- for name, host in host_dict.iteritems() %}
+{%- for name, host in host_dict.items() %}
 
 {%- if host.names is defined %}
 
 {%- set clearers = [] %}
-{%- for etc_addr, etc_names in salt.hosts.list_hosts().iteritems() %}
+{%- for etc_addr, etc_names in salt.hosts.list_hosts().items() %}
 {%- set names_to_clear = [] %}
 {%- for host_name in host.names %}
 {%- if (host.address != etc_addr) and host_name in etc_names %}
diff --git a/linux/network/init.sls b/linux/network/init.sls
index 56b05a5..8a7d458 100644
--- a/linux/network/init.sls
+++ b/linux/network/init.sls
@@ -16,6 +16,9 @@
 {%- if network.systemd|length > 0 %}
 - linux.network.systemd
 {%- endif %}
+{%- if network.openvswitch is defined %}
+- linux.network.openvswitch
+{%- endif %}
 {%- if network.interface|length > 0 %}
 - linux.network.interface
 {%- endif %}
diff --git a/linux/network/interface.sls b/linux/network/interface.sls
index 6ebc670..f2691a5 100644
--- a/linux/network/interface.sls
+++ b/linux/network/interface.sls
@@ -2,6 +2,11 @@
 {%- from "linux/map.jinja" import system with context %}
 {%- if network.enabled %}
 
+{%- if network.get('dpdk', {}).get('enabled', False) %}
+include:
+- linux.network.dpdk
+{%- endif %}
+
 {%- macro set_param(param_name, param_dict) -%}
 {%- if param_dict.get(param_name, False) -%}
 - {{ param_name }}: {{ param_dict[param_name] }}
@@ -53,10 +58,29 @@
 
 {%- endif %}
 
-{%- for interface_name, interface in network.interface.iteritems() %}
+{%- for interface_name, interface in network.interface.items() %}
 
 {%- set interface_name = interface.get('name', interface_name) %}
 
+{# add linux network interface into OVS dpdk bridge #}
+
+{%- if interface.type == 'dpdk_ovs_bridge' %}
+
+{%- for int_name, int in network.interface.items() %}
+
+{%- set int_name = int.get('name', int_name) %}
+
+{%- if int.ovs_bridge is defined and interface_name == int.ovs_bridge %}
+
+add_int_{{ int_name }}_to_ovs_dpdk_bridge_{{ interface_name }}:
+  cmd.run:
+    - unless: ovs-vsctl show | grep -w {{ int_name }}
+    - name: ovs-vsctl{%- if network.ovs_nowait %} --no-wait{%- endif %} add-port {{ interface_name }} {{ int_name }}
+
+{%- endif %}
+{%- endfor %}
+{%- endif %}
+
 {# it is not used for any interface with type preffix dpdk,eg. dpdk_ovs_port #}
 {%- if interface.get('managed', True) and not 'dpdk' in interface.type %}
 
@@ -69,7 +93,7 @@
   - name: {{ interface_name }}
 
 {# add linux network interface into OVS bridge #}
-{%- for int_name, int in network.interface.iteritems() %}
+{%- for int_name, int in network.interface.items() %}
 
 {%- set int_name = int.get('name', int_name) %}
 
@@ -78,7 +102,7 @@
 add_int_{{ int_name }}_to_ovs_bridge_{{ interface_name }}:
   cmd.run:
     - unless: ovs-vsctl show | grep {{ int_name }}
-    - name: ovs-vsctl add-port {{ interface_name }} {{ int_name }}
+    - name: ovs-vsctl{%- if network.ovs_nowait %} --no-wait{%- endif %} add-port {{ interface_name }} {{ int_name }}
 
 {%- endif %}
 
@@ -93,16 +117,20 @@
   - name: {{ interface_name }}
   - bridge: {{ interface.bridge }}
   - require:
+    {%- if network.interface.get(interface.bridge, {}).get('type', 'ovs_bridge') == 'dpdk_ovs_bridge' %}
+    - cmd: linux_network_dpdk_bridge_interface_{{ interface.bridge }}
+    {%- else %}
     - openvswitch_bridge: ovs_bridge_{{ interface.bridge }}
+    {%- endif %}
 
 ovs_port_set_type_{{ interface_name }}:
   cmd.run:
-  - name: ovs-vsctl set interface {{ interface_name }} type=patch
+  - name: ovs-vsctl{%- if network.ovs_nowait %} --no-wait{%- endif %} set interface {{ interface_name }} type=patch
   - unless: ovs-vsctl show | grep -A 1 'Interface {{ interface_name }}' | grep patch
 
 ovs_port_set_peer_{{ interface_name }}:
   cmd.run:
-  - name: ovs-vsctl set interface {{ interface_name }} options:peer={{ interface.peer }}
+  - name: ovs-vsctl{%- if network.ovs_nowait %} --no-wait{%- endif %} set interface {{ interface_name }} options:peer={{ interface.peer }}
   - unless: ovs-vsctl show | grep -A 2 'Interface {{ interface_name }}' | grep {{ interface.peer }}
 
 {%- else %}
@@ -221,15 +249,6 @@
   - mode: {{ interface.mode }}
   {%- endif %}
 
-{%- if interface.get('ipflush_onchange', False) %}
-
-linux_interface_ipflush_onchange_{{ interface_name }}:
-  cmd.run:
-  - name: "/sbin/ip address flush dev {{ interface_name }}; ifdown {{ interface_name }} ;ifup {{ interface_name }};"
-  - onchanges:
-    - network: linux_interface_{{ interface_name }}
-
-{%- endif %}
 
 {%- if salt['grains.get']('saltversion') < '2017.7' %}
 # TODO(ddmitriev): Remove this 'if .. endif' block completely when
@@ -330,7 +349,7 @@
   network.routes:
   - name: {{ interface_name }}
   - routes:
-    {%- for route_name, route in interface.route.iteritems() %}
+    {%- for route_name, route in interface.route.items() %}
     - name: {{ route_name }}
       ipaddr: {{ route.address }}
       netmask: {{ route.netmask }}
@@ -344,6 +363,34 @@
 
 {%- endif %}
 
+{%- if interface.type in ('eth','ovs_port') %}
+{%- if interface.get('ipflush_onchange', False) %}
+
+linux_interface_ipflush_onchange_{{ interface_name }}:
+  cmd.run:
+  - name: "/sbin/ip address flush dev {{ interface_name }}"
+{%- if interface.type == 'eth' %}
+  - onchanges:
+    - network: linux_interface_{{ interface_name }}
+{%- elif interface.type == 'ovs_port' %}
+  - onchanges:
+    - file: ovs_port_{{ interface_name }}
+{%- endif %}
+
+{%- if interface.get('restart_on_ipflush', False) %}
+
+linux_interface_restart_on_ipflush_{{ interface_name }}:
+  cmd.run:
+  - name: "ifdown {{ interface_name }}; ifup {{ interface_name }};"
+  - onchanges:
+    - cmd: linux_interface_ipflush_onchange_{{ interface_name }}
+
+{%- endif %}
+
+{%- endif %}
+
+{%- endif %}
+
 {%- endfor %}
 
 {%- if network.bridge != 'none' %}
diff --git a/linux/network/openvswitch.sls b/linux/network/openvswitch.sls
new file mode 100644
index 0000000..474a84c
--- /dev/null
+++ b/linux/network/openvswitch.sls
@@ -0,0 +1,26 @@
+{%- from "linux/map.jinja" import network with context %}
+
+{%- if network.get('openvswitch', {}).get('enabled', False) %}
+
+openvswitch_pkgs:
+  pkg.installed:
+    - pkgs: {{ network.ovs_pkgs }}
+
+/etc/default/openvswitch-switch:
+  file.managed:
+    - source: salt://linux/files/openvswitch-switch.default
+    - template: jinja
+    - require:
+      - pkg: openvswitch_pkgs
+
+openvswitch_switch_service:
+  service.running:
+    - name: openvswitch-switch
+    - enable: true
+    {%- if grains.get('noservices') %}
+    - onlyif: /bin/false
+    {%- endif %}
+    - watch:
+      - file: /etc/default/openvswitch-switch
+
+{%- endif %}
diff --git a/linux/network/resolv.sls b/linux/network/resolv.sls
index db1a463..caecee3 100644
--- a/linux/network/resolv.sls
+++ b/linux/network/resolv.sls
@@ -6,10 +6,13 @@
   - source: salt://linux/files/resolv.conf
   - mode: 644
   - template: jinja
+  - follow_symlinks: false
+  - require:
+    - service: resolvconf_service
 
-linux_resolvconf_disable:
-  cmd.run:
-  - name: resolvconf --disable-updates
-  - onlyif: resolvconf --updates-are-enabled
+resolvconf_service:
+  service.dead:
+    - name: resolvconf
+    - enable: false
 
 {%- endif %}
diff --git a/linux/network/systemd.sls b/linux/network/systemd.sls
index a8e1f24..68208f0 100644
--- a/linux/network/systemd.sls
+++ b/linux/network/systemd.sls
@@ -2,7 +2,7 @@
 {%- if network.enabled and grains.get('init', None) == 'systemd' %}
 
 {%- if network.systemd is mapping %}
-{%- for config_type, configs in network.systemd.iteritems() %}
+{%- for config_type, configs in network.systemd.items() %}
 
 {%- if config_type == 'link' %}
 /etc/udev/rules.d/80-net-setup-link.rules:
@@ -11,7 +11,7 @@
     - content: ""
 {%- endif %}
 
-{%- for config_name, config in configs.iteritems() %}
+{%- for config_name, config in configs.items() %}
 linux_network_systemd_networkd_{{ config_type }}_config_{{ config_name }}:
   file.managed:
     - name: /etc/systemd/network/{{ config_name }}.{{ config_type }}
diff --git a/linux/storage/disk.sls b/linux/storage/disk.sls
index 493b8df..a088aae 100644
--- a/linux/storage/disk.sls
+++ b/linux/storage/disk.sls
@@ -7,7 +7,7 @@
 xfsprogs:
   pkg.installed
 
-{%- for disk_name, disk in storage.disk.iteritems() %}
+{%- for disk_name, disk in storage.disk.items() %}
 {%- set disk_name = disk.name|default(disk_name) %}
 
 create_disk_label_{{ disk_name }}:
diff --git a/linux/storage/loopback.sls b/linux/storage/loopback.sls
index 34008e1..883b02e 100644
--- a/linux/storage/loopback.sls
+++ b/linux/storage/loopback.sls
@@ -6,8 +6,9 @@
 
 {%- if loopback.get('enabled', True) %}
 
-{{ salt['file.dirname'](loopback.file) }}:
+{{ loopback.file }}_directory:
   file.directory:
+  - name: {{ salt['file.dirname'](loopback.file) }}
   - makedirs: true
   - require_in:
     - file: {{ loopback.file }}
diff --git a/linux/storage/lvm.sls b/linux/storage/lvm.sls
index e88c2f1..6d1797d 100644
--- a/linux/storage/lvm.sls
+++ b/linux/storage/lvm.sls
@@ -5,7 +5,24 @@
   pkg.installed:
   - pkgs: {{ storage.lvm_pkgs }}
 
-{%- for vgname, vg in storage.lvm.iteritems() %}
+
+/etc/lvm/lvm.conf:
+  file.managed:
+  - source: salt://linux/files/lvm.conf
+  - template: jinja
+  - require:
+    - pkg: linux_lvm_pkgs
+
+lvm_services:
+  service.running:
+  - enable: true
+  - names: {{ storage.lvm_services }}
+  - require:
+    - file: /etc/lvm/lvm.conf
+  - watch:
+    - file: /etc/lvm/lvm.conf
+
+{%- for vgname, vg in storage.lvm.items() %}
 
 {%- if vg.get('enabled', True) %}
 
@@ -15,6 +32,8 @@
     - name: {{ dev }}
     - require:
       - pkg: linux_lvm_pkgs
+      - file: /etc/lvm/lvm.conf
+      - service: lvm_services
     - require_in:
       - lvm: lvm_vg_{{ vg.get('name', vgname) }}
 {%- endfor %}
@@ -24,7 +43,7 @@
     - name: {{ vg.get('name', vgname) }}
     - devices: {{ vg.devices|join(',') }}
 
-{%- for lvname, volume in vg.get('volume', {}).iteritems() %}
+{%- for lvname, volume in vg.get('volume', {}).items() %}
 
 lvm_{{ vg.get('name', vgname) }}_lv_{{ volume.get('name', lvname) }}:
   lvm.lv_present:
@@ -47,20 +66,4 @@
 
 {%- endfor %}
 
-/etc/lvm/lvm.conf:
-  file.managed:
-  - source: salt://linux/files/lvm.conf
-  - template: jinja
-  - require:
-    - pkg: linux_lvm_pkgs
-
-lvm_services:
-  service.running:
-  - enable: true
-  - names: {{ storage.lvm_services }}
-  - require:
-    - file: /etc/lvm/lvm.conf
-  - watch:
-    - file: /etc/lvm/lvm.conf
-
 {%- endif %}
diff --git a/linux/storage/swap.sls b/linux/storage/swap.sls
index f2e3a2d..7b8d82e 100644
--- a/linux/storage/swap.sls
+++ b/linux/storage/swap.sls
@@ -1,7 +1,7 @@
 {%- from "linux/map.jinja" import storage with context %}
 {%- if storage.enabled %}
 
-{%- for swap_name, swap in storage.swap.iteritems() %}
+{%- for swap_name, swap in storage.swap.items() %}
 
 {%- if swap.enabled %}
 
diff --git a/linux/system/apt.sls b/linux/system/apt.sls
index dca8135..0c3f7a7 100644
--- a/linux/system/apt.sls
+++ b/linux/system/apt.sls
@@ -7,7 +7,7 @@
 - linux.system.repo
 {%- endif %}
 
-{%- for key, config in system.apt.get('config', {}).iteritems() %}
+{%- for key, config in system.apt.get('config', {}).items() %}
 
 linux_apt_conf_{{ key }}:
   file.managed:
diff --git a/linux/system/auth.sls b/linux/system/auth.sls
new file mode 100644
index 0000000..2de2f6c
--- /dev/null
+++ b/linux/system/auth.sls
@@ -0,0 +1,192 @@
+{%- from "linux/map.jinja" import auth with context %}
+
+{%- if auth.enabled %}
+  {%- set pam_modules_enable = "" %}
+  {%- set pam_modules_disable = "" %}
+  {%- if grains.os_family == 'Debian' %}
+linux_auth_pam_packages:
+  pkg.installed:
+  - pkgs: [ 'libpam-runtime' ]
+
+linux_auth_pam_add_profile:
+  file.managed:
+    - name: /usr/local/bin/pam-add-profile
+    - source: salt://linux/files/pam-add-profile
+    - mode: 755
+    - require:
+      - pkg: linux_auth_pam_packages
+  {%- endif %}
+
+  {%- if auth.get('mkhomedir', {}).get('enabled', False) %}
+    {%- if grains.os_family == 'Debian' %}
+      {%- set pam_modules_enable = pam_modules_enable + ' mkhomedir' %}
+linux_auth_mkhomedir_debconf_package:
+  pkg.installed:
+  - pkgs: [ 'debconf-utils' ]
+
+linux_auth_mkhomedir_config:
+  file.managed:
+    - name: /usr/share/pam-configs/mkhomedir
+    - source: salt://linux/files/mkhomedir
+    - template: jinja
+
+    {%- endif %}
+  {%- else %}
+    {%- if grains.os_family == 'Debian' %}
+      {%- set pam_modules_disable = pam_modules_disable + ' mkhomedir' %}
+    {%- endif %}
+  {%- endif %}
+
+  {%- if auth.get('ldap', {}).get('enabled', False) %}
+    {%- from "linux/map.jinja" import ldap with context %}
+
+    {%- if grains.os_family == 'Debian' %}
+      {%- set pam_modules_enable = pam_modules_enable + ' ldap' %}
+
+linux_auth_ldap_debconf_package:
+  pkg.installed:
+  - pkgs: [ 'debconf-utils' ]
+
+linux_auth_debconf_libnss-ldapd:
+  debconf.set:
+    - name: libnss-ldapd
+    - data:
+        libnss-ldapd/nsswitch:
+          type: 'multiselect'
+          value: 'group, passwd, shadow'
+        libnss-ldapd/clean_nsswitch:
+          type: 'boolean'
+          value: 'false'
+    - require_in:
+      - pkg: linux_auth_ldap_packages
+    - require:
+      - pkg: linux_auth_ldap_debconf_package
+
+linux_auth_debconf_libpam-ldapd:
+  debconf.set:
+    - name: libpam-ldapd
+    - data:
+        libpam-ldapd/enable_shadow:
+          type: 'boolean'
+          value: 'true'
+    {%- endif %}
+  {%- else %}
+    {%- if grains.os_family == 'Debian' %}
+      {%- set pam_modules_disable = pam_modules_disable + ' ldap' %}
+    {%- endif %}
+  {%- endif %}
+
+  {#- Setup PAM profiles #}
+  {%- if grains.os_family == 'Debian' %}
+    {%- if auth.get('mkhomedir', {}).get('enabled', False) %}
+linux_auth_pam_add_profiles_mkhomedir_enable:
+  cmd.run:
+    - name: /usr/local/bin/pam-add-profile {{ pam_modules_enable }}
+    - unless: "[[ `grep -c pam_mkhomedir.so /etc/pam.d/common-session` -ne 0 ]]"
+    - require:
+      - file: linux_auth_pam_add_profile
+linux_auth_pam_add_profiles_mkhomedir_update:
+  cmd.wait:
+    - name: /usr/local/bin/pam-add-profile {{ pam_modules_enable }}
+    - watch:
+      - file: linux_auth_mkhomedir_config
+    - require:
+      - file: linux_auth_pam_add_profile
+      {%- if auth.get('ldap', {}).get('enabled', False) %}
+      - pkg: linux_auth_ldap_packages
+      {%- endif %}
+    {%- else %}
+linux_auth_pam_remove_profiles_mkhomedir:
+  cmd.run:
+    - name: /usr/sbin/pam-auth-update --remove {{ pam_modules_disable }}
+    - onlyif: "[[ `grep -c pam_mkhomedir.so /etc/pam.d/common-session` -ne 0 ]]"
+    - require:
+      - pkg: linux_auth_pam_packages
+    {%- endif %}
+
+    {%- if auth.get('ldap', {}).get('enabled', False) %}
+linux_auth_pam_add_profiles_ldap:
+  cmd.run:
+    - name: /usr/local/bin/pam-add-profile {{ pam_modules_enable }}
+    - unless: "[[ `debconf-get-selections | grep libpam-runtime/profiles | grep -c ldap` -ne 0 ]]"
+    - require:
+      - file: linux_auth_pam_add_profile
+      - pkg: linux_auth_ldap_packages
+    {%- else %}
+linux_auth_pam_remove_profiles_ldap:
+  cmd.run:
+    - name: /usr/sbin/pam-auth-update --remove {{ pam_modules_disable }}
+    - onlyif: "[[ `debconf-get-selections | grep libpam-runtime/profiles | grep -c ldap` -ne 0 ]]"
+    - require:
+      - pkg: linux_auth_pam_packages
+    {%- endif %}
+
+  {%- elif grains.os_family == 'RedHat' %}
+    {%- if auth.get('mkhomedir', {}).get('enabled', False) %}
+linux_auth_config_enable_mkhomedir:
+  cmd.run:
+    - name: "authconfig --enablemkhomedir --update"
+    - require:
+      {%- if auth.get('ldap', {}).get('enabled', False) %}
+      - pkg: linux_auth_ldap_packages
+      {%- endif %}
+    {%- else %}
+linux_auth_config_disable_mkhomedir:
+  cmd.run:
+    - name: "authconfig --disablemkhomedir --update"
+    - require:
+      - pkg: linux_auth_ldap_packages
+    {%- endif %}
+    {%- if auth.get('ldap', {}).get('enabled', False) %}
+linux_auth_config_enable_ldap:
+  cmd.run:
+    - name: "authconfig --enableldap --enableldapauth --update"
+    - require:
+      {%- if auth.get('ldap', {}).get('enabled', False) %}
+      - pkg: linux_auth_ldap_packages
+      {%- endif %}
+    {%- else %}
+linux_auth_config_disable_ldap:
+  cmd.run:
+    - name: "authconfig --disableldap --disableldapauth --update"
+    - require:
+      - pkg: linux_auth_ldap_packages
+    {%- endif %}
+  {%- endif %}
+
+  {%- if auth.get('ldap', {}).get('enabled', False) %}
+
+linux_auth_nsswitch_config_file:
+  file.managed:
+  - name: /etc/nsswitch.conf
+  - source: salt://linux/files/nsswitch.conf
+  - template: jinja
+  - mode: 644
+  - require:
+    - pkg: linux_auth_ldap_packages
+  - watch_in:
+    - service: linux_auth_nslcd_service
+
+linux_auth_ldap_packages:
+  pkg.installed:
+  - pkgs: {{ ldap.pkgs }}
+
+linux_auth_nslcd_config_file:
+  file.managed:
+  - name: /etc/nslcd.conf
+  - source: salt://linux/files/nslcd.conf
+  - template: jinja
+  - mode: 600
+  - require:
+    - pkg: linux_auth_ldap_packages
+  - watch_in:
+    - service: linux_auth_nslcd_service
+
+linux_auth_nslcd_service:
+  service.running:
+  - enable: true
+  - name: nslcd
+
+  {%- endif %}
+
+{%- endif %}
diff --git a/linux/system/banner.sls b/linux/system/banner.sls
new file mode 100644
index 0000000..6e283f5
--- /dev/null
+++ b/linux/system/banner.sls
@@ -0,0 +1,10 @@
+{%- from "linux/map.jinja" import banner with context %}
+
+{%- if banner.get('enabled', False) %}
+/etc/issue:
+  file.managed:
+  - user: root
+  - group: root
+  - mode: 644
+  - contents_pillar: linux:system:banner:contents
+{%- endif %}
diff --git a/linux/system/certificate.sls b/linux/system/certificate.sls
index ffa20c4..58ccb7e 100644
--- a/linux/system/certificate.sls
+++ b/linux/system/certificate.sls
@@ -8,7 +8,7 @@
     - name: ca-certificates
 {%- if system.ca_certificates is mapping %}
 
-{%- for name, cert in system.ca_certificates.iteritems() %}
+{%- for name, cert in system.ca_certificates.items() %}
 {{ system.ca_certs_dir }}/{{ name }}.crt:
   file.managed:
   - contents_pillar: "linux:system:ca_certificates:{{ name }}"
diff --git a/linux/system/cgroup.sls b/linux/system/cgroup.sls
new file mode 100644
index 0000000..caac31c
--- /dev/null
+++ b/linux/system/cgroup.sls
@@ -0,0 +1,119 @@
+{%- from "linux/map.jinja" import system with context %}
+
+{%- if system.cgroup.enabled|default(True) %}
+
+cgroup_package:
+  pkg.installed:
+  - pkgs:
+    - cgroup-bin
+
+include:
+  - linux.system.grub
+
+/etc/default/grub.d/80-cgroup.cfg:
+  file.managed:
+  - contents: 'GRUB_CMDLINE_LINUX_DEFAULT="quiet cgroup_enable=memory swapaccount=1"'
+  - require:
+    - file: grub_d_directory
+{%- if grains.get('virtual_subtype', None) not in ['Docker', 'LXC'] %}
+  - watch_in:
+    - cmd: grub_update
+{%- endif %}
+
+/etc/cgconfig.conf:
+  file.managed:
+  - user: root
+  - group: root
+  - mode: 0644
+  - template: jinja
+  - source: salt://linux/files/cgconfig.conf
+{%- if grains.get('virtual_subtype', None) not in ['Docker', 'LXC'] %}
+  - check_cmd: /usr/sbin/cgconfigparser -l
+{%- endif %}
+
+/etc/cgrules.conf:
+  file.managed:
+  - user: root
+  - group: root
+  - mode: 0644
+  - template: jinja
+  - source: salt://linux/files/cgrules.conf
+
+/etc/default/cgred:
+  file.managed:
+  - contents: |
+      OPTIONS=-v --logfile=/var/log/cgrulesengd.log
+
+/etc/systemd/system/cgred.service:
+  file.managed:
+  - contents: |
+      [Unit]
+      Description=CGroups Rules Engine Daemon
+      After=syslog.target
+
+      [Service]
+      Type=forking
+      EnvironmentFile=-/etc/default/cgred
+      ExecStart=/usr/sbin/cgrulesengd $OPTIONS
+
+      [Install]
+      WantedBy=multi-user.target
+
+cgred_service_running:
+  service.running:
+  - enable: true
+  - names: ['cgred']
+  - watch:
+    - file: /etc/cgconfig.conf
+    - file: /etc/cgrules.conf
+    - file: /etc/default/cgred
+    - file: /etc/systemd/system/cgred.service
+  {%- if grains.get('noservices') %}
+  - onlyif: /bin/false
+  {%- endif %}
+
+{%- else %}
+
+cgred_service_dead:
+  service.dead:
+  - enable: false
+  - names: ['cgred']
+  {%- if grains.get('noservices') %}
+  - onlyif: /bin/false
+  {%- endif %}
+
+include:
+  - linux.system.grub
+
+remove_/etc/default/grub.d/80-cgroup.cfg:
+  file.absent:
+  - name: /etc/default/grub.d/80-cgroup.cfg
+  - require:
+    - file: grub_d_directory
+{%- if grains.get('virtual_subtype', None) not in ['Docker', 'LXC'] %}
+  - watch_in:
+    - cmd: grub_update
+{%- endif %}
+
+remove_/etc/systemd/system/cgred.service:
+  file.absent:
+  - name: /etc/systemd/system/cgred.service
+
+remove_/etc/cgconfig.conf:
+  file.absent:
+  - name: /etc/cgconfig.conf
+
+remove_/etc/cgrules.conf:
+  file.absent:
+  - name: /etc/cgrules.conf
+
+remove_/etc/default/cgred:
+  file.absent:
+  - name: /etc/default/cgred
+
+purge_cgroup_package:
+  pkg.purged:
+  - pkgs:
+    - cgroup-tools
+
+{%- endif %}
diff --git a/linux/system/config.sls b/linux/system/config.sls
index 0c965da..8f13a71 100644
--- a/linux/system/config.sls
+++ b/linux/system/config.sls
@@ -3,13 +3,13 @@
 
 {%- if system.enabled %}
 
-  {%- for config_name, config in system.get('config', {}).iteritems() %}
+  {%- for config_name, config in system.get('config', {}).items() %}
     {%- if config.enabled|default(True) %}
       {%- for service_name in config.pillar.keys() %}
         {%- if pillar.get(service_name, {}).get('_support', {}).get('config', {}).get('enabled', False) %}
           {%- set support_fragment_file = service_name+'/meta/config.yml' %}
           {%- set service_config_files = load_support_file(support_fragment_file, config.pillar, config.get('grains', {}))|load_yaml %}
-          {%- for service_config_name, service_config in service_config_files.config.iteritems() %}
+          {%- for service_config_name, service_config in service_config_files.config.items() %}
 
 {{ service_config.path }}:
   file.managed:
@@ -24,7 +24,7 @@
     - defaults:
         pillar: {{ config.pillar|yaml }}
         grains: {{ config.get('grains', {}) }}
-        {%- for key, value in service_config.get('defaults', {}).iteritems() %}
+        {%- for key, value in service_config.get('defaults', {}).items() %}
         {{ key }}: {{ value }}
         {%- endfor %}
 
diff --git a/linux/system/console.sls b/linux/system/console.sls
index 3112bc5..8e5f3ab 100644
--- a/linux/system/console.sls
+++ b/linux/system/console.sls
@@ -3,7 +3,7 @@
 
 {%- if system.console is defined %}
 
-{%- for tty_name, console in system.console.iteritems() %}
+{%- for tty_name, console in system.console.items() %}
 
 {%- if grains.get('init', None) == 'upstart' %}
 {{ tty_name }}_service_file:
diff --git a/linux/system/directory.sls b/linux/system/directory.sls
index f53a870..81bff4d 100644
--- a/linux/system/directory.sls
+++ b/linux/system/directory.sls
@@ -1,11 +1,11 @@
 {%- from "linux/map.jinja" import system with context %}
 
-{%- for name, dir in system.directory.iteritems() %}
+{%- for name, dir in system.directory.items() %}
 
 {{ dir.name|default(name) }}:
   file.directory:
     {%- if dir %}
-      {%- for key, value in dir.iteritems() %}
+      {%- for key, value in dir.items() %}
     - {{ key }}: {{ value }}
       {%- endfor %}
     {%- else %}
diff --git a/linux/system/file.sls b/linux/system/file.sls
new file mode 100644
index 0000000..0de42fe
--- /dev/null
+++ b/linux/system/file.sls
@@ -0,0 +1,42 @@
+{%- from "linux/map.jinja" import system with context %}
+{%- if system.enabled %}
+
+{%- for file_name, file in system.file.items() %}
+
+linux_file_{{ file_name }}:
+  file.managed:
+    {%- if file.name is defined %}
+    - name: {{ file.name }}
+    {%- else %}
+    - name: {{ file_name }}
+    {%- endif %}
+    {%- if file.source is defined %}
+    - source: {{ file.source }}
+    {%- if file.hash is defined %}
+    - source_hash: {{ file.hash }}
+    {%- else %}
+    - skip_verify: True
+    {%- endif %}
+    {%- elif file.contents is defined %}
+    - contents: {{ file.contents|yaml }}
+    {%- elif file.contents_pillar is defined %}
+    - contents_pillar: {{ file.contents_pillar }}
+    {%- elif file.contents_grains is defined %}
+    - contents_grains: {{ file.contents_grains }}
+    {%- endif %}
+    - makedirs: {{ file.get('makedirs', 'True') }}
+    - user: {{ file.get('user', 'root') }}
+    - group: {{ file.get('group', 'root') }}
+    {%- if file.mode is defined %}
+    - mode: {{ file.mode }}
+    {%- endif %}
+    {%- if file.dir_mode is defined %}
+    - dir_mode: {{ file.dir_mode }}
+    {%- endif %}
+    {%- if file.encoding is defined %}
+    - encoding: {{ file.encoding }}
+    {%- endif %}
+
+{%- endfor %}
+
+{%- endif %}
diff --git a/linux/system/group.sls b/linux/system/group.sls
index 1a241a8..0e5545a 100644
--- a/linux/system/group.sls
+++ b/linux/system/group.sls
@@ -7,7 +7,7 @@
   {%- endif -%}
 {%- endmacro -%}
 
-{%- for group_name, group in system.group.iteritems() %}
+{%- for group_name, group in system.group.items() %}
 
 {%- if group.enabled %}
 
diff --git a/linux/system/hugepages.sls b/linux/system/hugepages.sls
index 669e254..54ccf45 100644
--- a/linux/system/hugepages.sls
+++ b/linux/system/hugepages.sls
@@ -3,7 +3,7 @@
 include:
   - linux.system.grub
 
-{%- if "pse" in grains.cpu_flags or "pdpe1gb" in grains.cpu_flags %}
+{%- if "pse" in grains.cpu_flags or "pdpe1gb" in grains.cpu_flags or "aarch64" in grains.cpuarch %}
 
 /etc/default/grub.d/90-hugepages.cfg:
   file.managed:
@@ -17,18 +17,24 @@
 
 {%- endif %}
 
-{%- for hugepages_type, hugepages in system.kernel.hugepages.iteritems() %}
-
-{%- if hugepages.get('mount', False) or hugepages.get('default', False) %}
+{%- for hugepages_type, hugepages in system.kernel.hugepages.items() %}
 
 hugepages_mount_{{ hugepages_type }}:
   mount.mounted:
     - name: {{ hugepages.mount_point }}
-    - device: Hugetlbfs-kvm
+    - device: Hugetlbfs-kvm-{{ hugepages.size|lower }}
     - fstype: hugetlbfs
     - mkmnt: true
     - opts: mode=775,pagesize={{ hugepages.size }}
+    - mount: {{ hugepages.mount|default('true') }}
 
+# Make hugepages available right away with a temporary systctl write
+# This will be handled via krn args after reboot, so don't use `sysctl.present`
+{%- if hugepages.get('default', False) %}
+hugepages_sysctl_vm_nr_hugepages:
+  cmd.run:
+    - name: "sysctl vm.nr_hugepages={{ hugepages.count }}"
+    - unless: "sysctl vm.nr_hugepages | grep -qE '{{ hugepages.count }}'"
 {%- endif %}
 
 {%- endfor %}
diff --git a/linux/system/init.sls b/linux/system/init.sls
index e17adc5..ad3681a 100644
--- a/linux/system/init.sls
+++ b/linux/system/init.sls
@@ -33,6 +33,9 @@
 {%- if system.sysfs is defined %}
 - linux.system.sysfs
 {%- endif %}
+{%- if system.cgroup is defined %}
+- linux.system.cgroup
+{%- endif %}
 {%- if system.locale|length > 0 %}
 - linux.system.locale
 {%- endif %}
@@ -99,6 +102,21 @@
 {%- if system.directory is defined %}
 - linux.system.directory
 {%- endif %}
+{%- if system.file is defined %}
+- linux.system.file
+{%- endif %}
+{%- if system.ld is defined %}
+- linux.system.ld
+{%- endif %}
 {%- if system.apt is defined and grains.os_family == 'Debian' %}
 - linux.system.apt
 {%- endif %}
+{%- if system.auth is defined %}
+- linux.system.auth
+{%- endif %}
+{%- if system.banner is defined %}
+- linux.system.banner
+{%- endif %}
+{%- if system.mcelog is defined %}
+- linux.system.mcelog
+{%- endif %}
diff --git a/linux/system/job.sls b/linux/system/job.sls
index 09ae0de..af42b58 100644
--- a/linux/system/job.sls
+++ b/linux/system/job.sls
@@ -4,7 +4,7 @@
 include:
 - linux.system.user
 
-{%- for name, job in system.job.iteritems() %}
+{%- for name, job in system.job.items() %}
 
 linux_job_{{ job.command }}:
   {%- if job.enabled|default(True) %}
diff --git a/linux/system/kernel.sls b/linux/system/kernel.sls
index 59b7177..0f50a19 100644
--- a/linux/system/kernel.sls
+++ b/linux/system/kernel.sls
@@ -3,41 +3,26 @@
 
 {%- if system.kernel is defined %}
 
-{%- if system.kernel.isolcpu is defined or system.kernel.elevator is defined %}
+{%- set kernel_boot_opts = [] %}
+{%- do kernel_boot_opts.append('isolcpus=' ~ system.kernel.isolcpu) if system.kernel.isolcpu is defined %}
+{%- do kernel_boot_opts.append('elevator=' ~ system.kernel.elevator) if system.kernel.elevator is defined %}
+{%- do kernel_boot_opts.extend(system.kernel.boot_options) if system.kernel.boot_options is defined %}
 
+{%- if kernel_boot_opts %}
 include:
   - linux.system.grub
 
-{%- if system.kernel.isolcpu is defined %}
-
-/etc/default/grub.d/90-isolcpu.cfg:
+/etc/default/grub.d/99-custom-settings.cfg:
   file.managed:
-    - contents: 'GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT isolcpus={{ system.kernel.isolcpu }}"'
-    - require:
-      - file: grub_d_directory
-{%- if grains.get('virtual_subtype', None) not in ['Docker', 'LXC'] %} 
-    - watch_in:
-      - cmd: grub_update
-
-{%- endif %}
-{%- endif %}
-
-{%- if system.kernel.elevator is defined %}
-
-/etc/default/grub.d/91-elevator.cfg:
-  file.managed:
-    - contents: 'GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT elevator={{ system.kernel.elevator }}"'
+    - contents: 'GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT {{ kernel_boot_opts|join(' ') }}"'
     - require:
       - file: grub_d_directory
 {%- if grains.get('virtual_subtype', None) not in ['Docker', 'LXC'] %}
     - watch_in:
       - cmd: grub_update
-
 {%- endif %}
 {%- endif %}
 
-{%- endif %}
-
 {%- if system.kernel.version is defined %}
 
 linux_kernel_package:
@@ -55,7 +40,7 @@
 # Not very Salt-ish.. :-(
 linux_kernel_old_absent:
   cmd.wait:
-  - name: "apt-get purge -y $(dpkg -l '*linux-image-[0-9]*' '*linux-headers-[0-9]*' '*linux-image-extra-[0-9]*' | grep -E '^ii' | awk '{print $2}' | grep -v '{{ system.kernel.version }}')"
+  - name: "dpkg -l '*linux-generic-*[0-9]*' '*linux-image-*[0-9]*' '*linux-headers-*[0-9]*' '*linux-image-extra-*[0-9]*' | grep -E '^ii' | awk '{print $2}' | grep -v '{{ system.kernel.version }}' | xargs dpkg --purge --force-depends"
   - watch:
     - pkg: linux_kernel_package
 
@@ -71,7 +56,7 @@
 
 {%- endfor %}
 
-{%- for module_name, module_content in system.kernel.get('module', {}).iteritems() %}
+{%- for module_name, module_content in system.kernel.get('module', {}).items() %}
 
 /etc/modprobe.d/{{ module_name }}.conf:
   file.managed:
@@ -86,7 +71,7 @@
 
 {%- endfor %}
 
-{%- for sysctl_name, sysctl_value in system.kernel.get('sysctl', {}).iteritems() %}
+{%- for sysctl_name, sysctl_value in system.kernel.get('sysctl', {}).items() %}
 
 linux_kernel_{{ sysctl_name }}:
   sysctl.present:
diff --git a/linux/system/ld.sls b/linux/system/ld.sls
new file mode 100644
index 0000000..fb239da
--- /dev/null
+++ b/linux/system/ld.sls
@@ -0,0 +1,23 @@
+{%- from "linux/map.jinja" import system with context %}
+
+{%- if system.enabled %}
+
+{%- for key in system.ld.library %}
+/etc/ld.so.conf.d/{{ key }}.conf:
+  file.managed:
+    - user: root
+    - group: root
+    - mode: 644
+    - contents: |
+          {% for val in system.ld.library[key] -%}
+          {{ val }}
+          {% endfor %}
+    - watch_in:
+      - cmd: ldconfig_update
+{% endfor %}
+
+ldconfig_update:
+  cmd.wait:
+  - name: ldconfig
+
+{% endif %}
diff --git a/linux/system/limit.sls b/linux/system/limit.sls
index bb29268..8f424d8 100644
--- a/linux/system/limit.sls
+++ b/linux/system/limit.sls
@@ -1,7 +1,7 @@
 {%- from "linux/map.jinja" import system with context %}
 {%- if system.enabled %}
 
-{%- for name, limit in system.limit.iteritems() %}
+{%- for name, limit in system.limit.items() %}
 
 linux_limit_{{ name }}:
   {%- if limit.get('enabled', True) %}
diff --git a/linux/system/locale.sls b/linux/system/locale.sls
index 2941c17..656aeb3 100644
--- a/linux/system/locale.sls
+++ b/linux/system/locale.sls
@@ -1,7 +1,7 @@
 {%- from "linux/map.jinja" import system with context %}
 {%- if system.enabled %}
 
-{%- for locale_name, locale in system.locale.iteritems() %}
+{%- for locale_name, locale in system.locale.items() %}
 {%- if locale.get('enabled', True) %}
 
 linux_locale_{{ locale_name }}:
diff --git a/linux/system/mcelog.sls b/linux/system/mcelog.sls
new file mode 100644
index 0000000..c2d0fd4
--- /dev/null
+++ b/linux/system/mcelog.sls
@@ -0,0 +1,32 @@
+{%- from "linux/map.jinja" import system with context %}
+{%- if system.enabled %}
+
+{%- if system.get('mcelog',{}).get('enabled', False) %}
+
+mcelog_packages:
+  pkg.installed:
+    - name: mcelog
+
+mcelog_conf:
+  file.managed:
+    - name: /etc/mcelog/mcelog.conf
+    - source: salt://linux/files/mcelog.conf
+    - template: jinja
+    - user: root
+    - group: root
+    - mode: 644
+    - require:
+      - pkg: mcelog_packages
+
+mce_service:
+  service.running:
+  - name: mcelog
+  - enable: true
+  - require:
+    - pkg: mcelog_packages
+  - watch:
+    - file: mcelog_conf
+
+{%- endif %}
+
+{%- endif %}
diff --git a/linux/system/motd.sls b/linux/system/motd.sls
index f6cdc47..4224407 100644
--- a/linux/system/motd.sls
+++ b/linux/system/motd.sls
@@ -1,27 +1,19 @@
 {%- from "linux/map.jinja" import system with context %}
-{%- if system.enabled %}
+{%- if system.enabled and system.motd|length > 0 %}
 
-{%- if grains.os_family == 'RedHat' %}
+/etc/update-motd.d:
+  file.directory:
+    - clean: true
 
-{#- update-motd is not available in RedHat, so support only static motd #}
+{%- if system.motd is string %}
+
+{#- Set static motd only #}
 /etc/motd:
   file.managed:
     - contents_pillar: linux:system:motd
 
 {%- else %}
 
-{%- if grains.os == 'Ubuntu' %}
-package_update_motd:
-  pkg.installed:
-    - name: update-motd
-    - require_in:
-      - file: /etc/update-motd.d
-{%- endif %}
-
-/etc/update-motd.d:
-  file.directory:
-    - clean: true
-
 {%- if grains.oscodename == "jessie" %}
 motd_fix_pam_sshd:
   file.replace:
@@ -36,7 +28,7 @@
 {%- for motd in system.motd %}
 {%- set motd_index = loop.index %}
 
-{%- for name, value in motd.iteritems() %}
+{%- for name, value in motd.items() %}
 motd_{{ motd_index }}_{{ name }}:
   file.managed:
     - name: /etc/update-motd.d/5{{ motd_index }}-{{ name }}
diff --git a/linux/system/netconsole.sls b/linux/system/netconsole.sls
index 92dab5d..0df0e83 100644
--- a/linux/system/netconsole.sls
+++ b/linux/system/netconsole.sls
@@ -24,7 +24,7 @@
     - template: jinja
 
 {% if system.netconsole is mapping and system.netconsole.target is mapping %}
-{% for target, data in system.netconsole.target.iteritems() %}
+{% for target, data in system.netconsole.target.items() %}
 {% if data is mapping and data.interface is defined %}
 /etc/network/if-up.d/netconsole {{ target }} {{ data.interface }}:
   cmd.run:
diff --git a/linux/system/package.sls b/linux/system/package.sls
index 4f42ec8..276194c 100644
--- a/linux/system/package.sls
+++ b/linux/system/package.sls
@@ -7,7 +7,7 @@
   'removed': [],
   'installed': [],
   } %}
-{%- for name, package in system.package.iteritems() %}
+{%- for name, package in system.package.items() %}
 
   {%- if package.repo is defined or package.hold is defined or package.verify is defined %}
 linux_extra_package_{{ name }}:
@@ -47,7 +47,7 @@
 
 {%- endfor %}
 
-{%- for pkgs_group, pkgs in pkgs_groups.iteritems() %}
+{%- for pkgs_group, pkgs in pkgs_groups.items() %}
   {%- if pkgs %}
 linux_extra_packages_{{ pkgs_group }}:
   pkg.{{ pkgs_group }}:
diff --git a/linux/system/profile.sls b/linux/system/profile.sls
index 7e1fb28..bc74489 100644
--- a/linux/system/profile.sls
+++ b/linux/system/profile.sls
@@ -9,7 +9,7 @@
 
 {%- if system.profile|length > 0 %}
 
-{%- for name, script in system.profile.iteritems() %}
+{%- for name, script in system.profile.items() %}
 profile.d_script_{{ name  }}:
     file.managed:
     - name: /etc/profile.d/salt_profile_{{ name }}{%if name.split('.')|length == 1 %}.sh{% endif %}
diff --git a/linux/system/repo.sls b/linux/system/repo.sls
index 1ea921c..ace6dab 100644
--- a/linux/system/repo.sls
+++ b/linux/system/repo.sls
@@ -38,7 +38,7 @@
 
 {%- endif %}
 
-{%- for name, repo in system.repo.iteritems() %}
+{%- for name, repo in system.repo.items() %}
 {%- set name=repo.get('name', name) %}
 {%- if grains.os_family == 'Debian' %}
 
@@ -108,7 +108,7 @@
   {%- if repo.ppa is defined %}
   - ppa: {{ repo.ppa }}
   {%- else %}
-  - human_name: {{ name }}
+  - humanname: {{ name }}
   - name: {{ repo.source }}
   {%- if repo.architectures is defined %}
   - architectures: {{ repo.architectures }}
@@ -143,6 +143,18 @@
 {%- else %}
 
 linux_repo_{{ name }}_absent:
+  pkgrepo.absent:
+    {%- if repo.ppa is defined %}
+    - ppa: {{ repo.ppa }}
+    {%- if repo.key_id is defined %}
+    - keyid_ppa: {{ repo.keyid_ppa }}
+    {%- endif %}
+    {%- else %}
+    - file: /etc/apt/sources.list.d/{{ name }}.list
+    {%- if repo.key_id is defined %}
+    - keyid: {{ repo.key_id }}
+    {%- endif %}
+    {%- endif %}
   file.absent:
     - name: /etc/apt/sources.list.d/{{ name }}.list
 
@@ -150,17 +162,19 @@
 
 {%- endif %}
 
+{#- os_family Debian #}
 {%- endif %}
 
 {%- if grains.os_family == "RedHat" %}
 
+{%- if repo.get('enabled', True) %}
+
 {%- if repo.get('proxy', {}).get('enabled', False) %}
 # PLACEHOLDER
 # TODO, implement per proxy configuration for Yum
 {%- endif %}
 
 {%- if not repo.get('default', False) %}
-
 linux_repo_{{ name }}:
   pkgrepo.managed:
   - name: {{ name }}
@@ -176,11 +190,18 @@
   {%- endif %}
   - require:
     - pkg: linux_repo_prereq_pkgs
-
 {%- endif %}
 
+{#- repo.enabled is false #}
+{%- else %}
+  pkgrepo.absent:
+    - name: {{ repo.source }}
 {%- endif %}
 
+{#- os_family Redhat #}
+{%- endif %}
+
+{#- repo.items() loop #}
 {%- endfor %}
 
 {%- if default_repos|length > 0 and grains.os_family == 'Debian' %}
diff --git a/linux/system/service.sls b/linux/system/service.sls
index 869760d..a6f9f02 100644
--- a/linux/system/service.sls
+++ b/linux/system/service.sls
@@ -1,7 +1,7 @@
 {%- from "linux/map.jinja" import system with context %}
 {%- if system.enabled %}
 
-{%- for name, service in system.service.iteritems() %}
+{%- for name, service in system.service.items() %}
 
 linux_service_{{ name }}:
   service.{{ service.status }}:
diff --git a/linux/system/sysfs.sls b/linux/system/sysfs.sls
index fdf1686..71c560b 100644
--- a/linux/system/sysfs.sls
+++ b/linux/system/sysfs.sls
@@ -11,7 +11,7 @@
     - require:
       - pkg: linux_sysfs_package
 
-{%- for name, sysfs in system.get('sysfs', {}).iteritems() %}
+{%- for name, sysfs in system.get('sysfs', {}).items() %}
 
 /etc/sysfs.d/{{ name }}.conf:
   file.managed:
@@ -26,7 +26,7 @@
     - require:
       - file: /etc/sysfs.d
 
-  {%- for key, value in sysfs.iteritems() %}
+  {%- for key, value in sysfs.items() %}
     {%- if key not in ["mode", "owner"] %}
       {%- if grains.get('virtual_subtype', None) not in ['Docker', 'LXC'] %}
       {#- Sysfs cannot be set in docker, LXC, etc. #}
diff --git a/linux/system/user.sls b/linux/system/user.sls
index fc64038..efda1ba 100644
--- a/linux/system/user.sls
+++ b/linux/system/user.sls
@@ -4,7 +4,7 @@
 include:
   - linux.system.group
 
-{%- for name, user in system.user.iteritems() %}
+{%- for name, user in system.user.items() %}
 
 {%- if user.enabled %}
 
@@ -15,6 +15,14 @@
   {%- endif %}
 {%- endfor %}
 
+{%- if user.gid is not defined %}
+system_group_{{ name }}:
+  group.present:
+  - name: {{ name }}
+  - require_in:
+    - user: system_user_{{ name }}
+{%- endif %}
+
 system_user_{{ name }}:
   user.present:
   - name: {{ name }}
@@ -71,7 +79,7 @@
 
 /etc/sudoers.d/90-salt-user-{{ name|replace('.', '-') }}:
   file.absent
-  
+
 {%- endif %}
 
 {%- else %}
diff --git a/metadata.yml b/metadata.yml
index 4bbfca2..d87f5a6 100644
--- a/metadata.yml
+++ b/metadata.yml
@@ -1,3 +1,3 @@
 name: "linux"
 version: "2017.4.1"
-source: "https://github.com/tcpcloud/salt-formula-linux"
+source: "https://github.com/salt-formulas/salt-formula-linux"
diff --git a/tests/pillar/network_openvswitch_dpdk.sls b/tests/pillar/network_openvswitch_dpdk.sls
index d4e6785..be8ed98 100644
--- a/tests/pillar/network_openvswitch_dpdk.sls
+++ b/tests/pillar/network_openvswitch_dpdk.sls
@@ -20,6 +20,11 @@
         name: "openvswitch-vhost"
         path: "/run/openvswitch-vhost"
     interface:
+      eth0:
+        enabled: true
+        type: eth
+        proto: manual
+        ovs_bridge: br-prv
       dpdk0:
         name: enp5s0f1
         pci: "0000:05:00.1"
@@ -34,14 +39,39 @@
         bond: dpdkbond0
         enabled: true
         type: dpdk_ovs_port
+      dpdk2:
+        name: enp6s0f1
+        pci: "0000:06:00.1"
+        driver: igb_uio
+        bond: dpdkbond1
+        enabled: true
+        type: dpdk_ovs_port
+      dpdk3:
+        name: enp6s0f2
+        pci: "0000:06:00.2"
+        driver: igb_uio
+        bond: dpdkbond1
+        enabled: true
+        type: dpdk_ovs_port
       dpdkbond0:
         enabled: true
         bridge: br-prv
         type: dpdk_ovs_bond
         mode: active-backup
+      dpdkbond1:
+        enabled: true
+        bridge: br-mesh
+        type: dpdk_ovs_bond
+        mode: balance-slb
       br-prv:
         enabled: true
         type: dpdk_ovs_bridge
+      br-mesh:
+        tag: 1302
+        enabled: true
+        type: dpdk_ovs_bridge
+        address: 1.2.3.4
+        netmask: 255.255.255.252
       dummy0:
         enabled: true
         name: dummy0
diff --git a/tests/pillar/system.sls b/tests/pillar/system.sls
index 06a1bdc..8aeb9d7 100644
--- a/tests/pillar/system.sls
+++ b/tests/pillar/system.sls
@@ -19,6 +19,19 @@
     kernel:
       isolcpu: 1,2,3,4
       elevator: deadline
+      boot_options:
+        - pti=off
+        - spectre_v2=auto
+    cgroup:
+      group:
+        group_1:
+          controller:
+            cpu:
+              shares:
+                value: 250
+          mapping:
+            subjects:
+            - '@group1'
     sysfs:
       scheduler:
         block/sda/queue/scheduler: deadline
@@ -51,6 +64,7 @@
       testuser:
         enabled: true
         name: testuser
+        password: passw0rd
         sudo: true
         uid: 9999
         full_name: Test User
@@ -190,10 +204,10 @@
         key_url: http://apt-mk.mirantis.com/public.gpg
         architectures: amd64
     locale:
-      en_US.UTF-8:
+      en_US:
         enabled: true
         default: true
-      "cs_CZ.UTF-8 UTF-8":
+      cs_CZ:
         enabled: true
     autoupdates:
       enabled: true
@@ -355,3 +369,8 @@
       interval: 20
       logpath: "/var/mylog/atop"
       outfile: "/var/mylog/atop/daily.log"
+    mcelog:
+      enabled: true
+      logging:
+        syslog: true
+        syslog_error: true
diff --git a/tests/pillar/system_banner.sls b/tests/pillar/system_banner.sls
new file mode 100644
index 0000000..6c9ca6a
--- /dev/null
+++ b/tests/pillar/system_banner.sls
@@ -0,0 +1,10 @@
+linux:
+  system:
+    enabled: true
+    banner:
+      enabled: true
+      contents: |
+        ================= WARNING =================
+        This is tcpcloud network.
+        Unauthorized access is strictly prohibited.
+        ===========================================
diff --git a/tests/pillar/system_extra.sls b/tests/pillar/system_extra.sls
index 801c628..591e60b 100644
--- a/tests/pillar/system_extra.sls
+++ b/tests/pillar/system_extra.sls
@@ -1,6 +1,24 @@
 
 linux:
   system:
+    auth:
+      enabled: true
+      mkhomedir:
+        enabled: true
+        umask: 0027
+      ldap:
+        enabled: true
+        binddn: cn=bind,ou=service_users,dc=example,dc=com
+        bindpw: secret
+        uri: ldap://127.0.0.1
+        base: ou=users,dc=example,dc=com
+        ldap_version: 3
+        pagesize: 65536
+        referrals: off
+        filter:
+          passwd: (&(&(objectClass=person)(uidNumber=*))(unixHomeDirectory=*))
+          shadow: (&(&(objectClass=person)(uidNumber=*))(unixHomeDirectory=*))
+          group:  (&(objectClass=group)(gidNumber=*))
     enabled: true
     cluster: default
     name: linux
diff --git a/tests/pillar/system_file.sls b/tests/pillar/system_file.sls
new file mode 100644
index 0000000..8de464b
--- /dev/null
+++ b/tests/pillar/system_file.sls
@@ -0,0 +1,21 @@
+linux:
+  system:
+    enabled: true
+    file:
+      /tmp/sample.txt:
+        source: http://techslides.com/demos/samples/sample.txt
+        source_hash: 5452459724e85b4e12277d5f8aab8fc9
+      sample2.txt:
+        name: /tmp/sample2.txt
+        source: http://techslides.com/demos/samples/sample.txt
+      test2:
+        name: /tmp/test2.txt
+        contents: |
+          line1
+          line2
+        user: root
+        group: root
+        mode: 700
+        dir_mode: 700
+        encoding: utf-8
+        makedirs: true
\ No newline at end of file
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index 9451611..316692b 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -37,7 +37,7 @@
     log_info "Setting up Python virtualenv"
     virtualenv $VENV_DIR
     source ${VENV_DIR}/bin/activate
-    pip install salt${PIP_SALT_VERSION}
+    python -m pip install salt${PIP_SALT_VERSION}
 }
 
 setup_pillar() {
@@ -111,7 +111,7 @@
 
 salt_run() {
     [ -e ${VENV_DIR}/bin/activate ] && source ${VENV_DIR}/bin/activate
-    salt-call ${SALT_OPTS} $*
+    python $(which salt-call) ${SALT_OPTS} $*
 }
 
 prepare() {