Merge "Adds alert to warn for open files being depleted"
diff --git a/README.rst b/README.rst
index a4d5323..b90c0f5 100644
--- a/README.rst
+++ b/README.rst
@@ -328,6 +328,19 @@
               DefaultLimitCPU: 2
               DefaultLimitNPROC: 4
 
+Ensure presence of directory:
+
+.. code-block:: yaml
+
+    linux:
+      system:
+        directory:
+          /tmp/test:
+            user: root
+            group: root
+            mode: 700
+            makedirs: true
+
 Kernel
 ~~~~~~
 
@@ -613,6 +626,21 @@
       system:
         purge_repos: true
 
+Setup custom apt config options:
+
+.. code-block:: yaml
+
+    linux:
+      system:
+        apt:
+          config:
+            compression-workaround:
+              "Acquire::CompressionTypes::Order": "gz"
+            docker-clean:
+              "DPkg::Post-Invoke":
+                - "rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true"
+              "APT::Update::Post-Invoke":
+                - "rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true"
 
 RC
 ~~
@@ -1409,7 +1437,7 @@
                   mount: ${linux:storage:mount:data}
 
 Create partitions on disk. Specify size in MB. It expects empty
-disk without any existing partitions.
+disk without any existing partitions. (set startsector=1, if you want to start partitions from 2048)
 
 .. code-block:: yaml
 
@@ -1417,6 +1445,7 @@
         storage:
           disk:
             first_drive:
+              startsector: 1
               name: /dev/loop1
               type: gpt
               partitions:
diff --git a/linux/files/60-net-txqueue.rules b/linux/files/60-net-txqueue.rules
index 7805c9c..8f6478c 100644
--- a/linux/files/60-net-txqueue.rules
+++ b/linux/files/60-net-txqueue.rules
@@ -1 +1 @@
-KERNEL==”tap[0-9a-z\-]*", RUN+="/sbin/ip link set %k txqueuelen {{ network.tap_custom_txqueuelen }}"
\ No newline at end of file
+KERNEL=="tap[0-9a-z\-]*", RUN+="/sbin/ip link set %k txqueuelen {{ tap_custom_txqueuelen }}"
diff --git a/linux/files/apt.conf b/linux/files/apt.conf
new file mode 100644
index 0000000..709fbd6
--- /dev/null
+++ b/linux/files/apt.conf
@@ -0,0 +1,9 @@
+// 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() %}
+{{ key }} {% if value is iterable and value is not string %}{ {% for entry in value %}"{{ entry }}";{% endfor %} } {% else %}{{ value }};{% endif %}
+{%- endfor %}
+{#-
+vim: syntax=jinja
+-#}
diff --git a/linux/files/systemd.conf b/linux/files/systemd.conf
index 36e01a5..2663aed 100644
--- a/linux/files/systemd.conf
+++ b/linux/files/systemd.conf
@@ -1,5 +1,5 @@
 {%- from "linux/map.jinja" import system with context -%}
-{%- for section, options in settings.iteritems() -%}
+{%- for section, options in settings.iteritems() %}
 [{{ section }}]
 {%- for option, value in options.iteritems() %}
 {{ option }}={{ value }}
diff --git a/linux/map.jinja b/linux/map.jinja
index de250bb..812033a 100644
--- a/linux/map.jinja
+++ b/linux/map.jinja
@@ -18,6 +18,7 @@
          },
         'selinux': 'permissive',
         'ca_certs_dir': '/usr/local/share/ca-certificates',
+        'ca_certs_bin': 'update-ca-certificates',
         'atop': {
              'enabled': false,
              'interval': '20',
@@ -45,6 +46,7 @@
          },
         'selinux': 'permissive',
         'ca_certs_dir': '/usr/local/share/ca-certificates',
+        'ca_certs_bin': 'update-ca-certificates',
         'atop': {
              'enabled': false,
              'interval': '20',
@@ -71,7 +73,8 @@
              'pkgs': []
          },
         'selinux': 'permissive',
-        'ca_certs_dir': '/usr/local/share/ca-certificates',
+        'ca_certs_dir': '/etc/pki/ca-trust/source/anchors',
+        'ca_certs_bin': 'update-ca-trust extract',
         'atop': {
              'enabled': false,
              'interval': '20',
@@ -114,6 +117,7 @@
     'hashing-algorithm',
     'hardware-dma-ring-rx',
     'hwaddr',
+    'noifupdown',
 ] %}
 
 {% set network = salt['grains.filter_by']({
diff --git a/linux/meta/fluentd.yml b/linux/meta/fluentd.yml
new file mode 100644
index 0000000..6b4041c
--- /dev/null
+++ b/linux/meta/fluentd.yml
@@ -0,0 +1,47 @@
+{%- if grains.get('init') == 'systemd' %}
+agent:
+  plugin:
+    fluent-plugin-systemd:
+      gem: ['fluent-plugin-systemd']
+  config:
+    label:
+      systemd:
+        input:
+          systemd:
+            type: systemd
+            tag: systemd.source
+            path: /run/log/journal
+            pos_file: {{ pillar.fluentd.agent.dir.positiondb }}/systemd.source.pos
+            entry:
+              field_map:
+                MESSAGE: 'Payload'
+                _CMDLINE: 'process'
+                _PID: 'Pid'
+                _COMM: 'programname'
+                _SYSTEMD_UNIT: 'service'
+                syslog_identifier: 'ident'
+                priority: 'Severity'
+              field_map_strict: True
+              fields_strip_underscores: True
+              fields_lowercase: True
+        filter:
+          add_severity_label:
+            tag: systemd.source
+            type: record_transformer
+            enable_ruby: true
+            record:
+              - name: severity_label
+                value: '${ {"TRACE"=>8,"DEBUG"=>7,"INFO"=>6,"NOTICE"=>5,"WARNING"=>4,"ERROR"=>3,"CRITICAL"=>2,"ALERT"=>1,"EMERGENCY"=>0}.key(record["Severity"].to_i) }'
+        match:
+          rewrite_tag:
+            tag: systemd.source
+            type: rewrite_tag_filter
+            rule:
+              - name: service
+                regexp: '^(.*)\.(.*)$'
+                result: __TAG__.$1
+          push_to_default:
+            tag: 'systemd.source.*'
+            type: relabel
+            label: default_output
+{%- endif %}
diff --git a/linux/meta/meta.yml b/linux/meta/meta.yml
index 632b910..8d17c54 100644
--- a/linux/meta/meta.yml
+++ b/linux/meta/meta.yml
@@ -7,10 +7,12 @@
   relations:
   {%- if system.repo is defined %}
   {%- for repo_name, repo in system.repo.iteritems() %}
+  {%- if repo.get('enabled', True) %}
   - service: apt.repo
     host_external: {{ repo.source }}
     direction: source
     type: tcp-http
+  {%- endif %}
   {%- endfor %}
   {%- endif %}
 {%- endif %}
diff --git a/linux/meta/prometheus.yml b/linux/meta/prometheus.yml
index d2f812f..e8a26e3 100644
--- a/linux/meta/prometheus.yml
+++ b/linux/meta/prometheus.yml
@@ -14,6 +14,7 @@
     SystemDiskSpaceTooLow:
       if: 'predict_linear(disk_free[1h], 8*3600) < 0'
       {% raw %}
+      for: 15m
       labels:
         severity: warning
         service: system
@@ -44,6 +45,7 @@
     SystemDiskInodesTooLow:
       if: 'predict_linear(disk_inodes_free[1h], 8*3600) < 0'
       {% raw %}
+      for: 15m
       labels:
         severity: warning
         service: system
@@ -52,7 +54,7 @@
         description: 'The disk inodes ({{ $labels.path }}) will be full in less than 8 hours on {{ $labels.host }}.'
       {% endraw %}
     SystemDiskInodesFull:
-      if: 'disk_inodes_used / disk_inodes_total >= 99'
+      if: 'disk_inodes_used / disk_inodes_total >= 0.99'
       {% raw %}
       labels:
         severity: critical
diff --git a/linux/meta/salt.yml b/linux/meta/salt.yml
index 9ca9ee9..20c9be9 100644
--- a/linux/meta/salt.yml
+++ b/linux/meta/salt.yml
@@ -12,9 +12,12 @@
     {%- for service_name, service in pillar.items() %}
       {%- if service.get('_support', {}).get('sphinx', {}).get('enabled', False) %}
         {%- set grains_fragment_file = service_name+'/meta/sphinx.yml' %}
-        {%- macro load_grains_file() %}{% include grains_fragment_file %}{% endmacro %}
-        {%- set grains_yaml = load_grains_file()|load_yaml %}
-        {%- do service_grains.sphinx.doc.update({ service_name: grains_yaml.doc }) %}
+        {%- macro load_grains_file() %}{% include grains_fragment_file ignore missing %}{% endmacro %}
+        {%- set grains = load_grains_file()|load_yaml %}
+        {%- if grains %}
+          {%- set grains_yaml = load_grains_file()|load_yaml %}
+          {%- do service_grains.sphinx.doc.update({ service_name: grains_yaml.doc }) %}
+        {%- endif %}
       {%- endif %}
     {%- endfor %}
     {{ service_grains|yaml(False)|indent(4) }}
diff --git a/linux/network/dhclient.sls b/linux/network/dhclient.sls
index 6de2cfd..c0b007b 100644
--- a/linux/network/dhclient.sls
+++ b/linux/network/dhclient.sls
@@ -8,4 +8,11 @@
     - source: salt://linux/files/dhclient.conf
     - template: jinja
 
+{%- elif network.dhclient.enabled is defined and network.dhclient.enabled == False %}
+
+kill_dhcp_client:
+  cmd.run:
+  - name: "pkill dhclient"
+  - onlyif: "pgrep dhclient"
+
 {%- endif %}
diff --git a/linux/network/dpdk.sls b/linux/network/dpdk.sls
index 751941c..1ac9e25 100644
--- a/linux/network/dpdk.sls
+++ b/linux/network/dpdk.sls
@@ -55,6 +55,10 @@
 ]
 %}
 
+{%- if network.openvswitch.get('vhost_socket_dir',{}).get('path') %}
+{%- do ovs_options.append("vhost-sock-dir=\""+network.openvswitch.vhost_socket_dir.path+"\"") %}
+{%- endif %}
+
 {%- for option in ovs_options %}
 
 linux_network_dpdk_ovs_option_{{ option }}:
diff --git a/linux/network/interface.sls b/linux/network/interface.sls
index 6f37348..921ceac 100644
--- a/linux/network/interface.sls
+++ b/linux/network/interface.sls
@@ -45,6 +45,13 @@
 
 {%- endfor %}
 
+{%- if network.interface is defined %}
+
+remove_cloud_init_file:
+  file.absent:
+  - name: /etc/network/interfaces.d/50-cloud-init.cfg
+
+{%- endif %}
 
 {%- for interface_name, interface in network.interface.iteritems() %}
 
@@ -121,7 +128,7 @@
 ovs_port_{{ interface_name }}_line1:
   file.replace:
   - name: /etc/network/interfaces
-  - pattern: auto {{ interface_name }}
+  - pattern: auto {{ interface_name }}$
   - repl: ""
 
 ovs_port_{{ interface_name }}_line2:
@@ -218,7 +225,7 @@
 
 linux_interface_ipflush_onchange_{{ interface_name }}:
   cmd.run:
-  - name: "/sbin/ip address flush dev {{ interface_name }}"
+  - name: "/sbin/ip address flush dev {{ interface_name }}; ifdown {{ interface_name }} ;ifup {{ interface_name }};"
   - onchanges:
     - network: linux_interface_{{ interface_name }}
 
@@ -246,7 +253,7 @@
 remove_interface_{{ network }}_line1:
   file.replace:
   - name: /etc/network/interfaces
-  - pattern: auto {{ network }}
+  - pattern: auto {{ network }}$
   - repl: ""
 
 remove_interface_{{ network }}_line2:
@@ -346,6 +353,14 @@
       # Workaround for Upstream-Bug: https://github.com/saltstack/salt/issues/40262
       source /etc/network/interfaces.u/*
 
+linux_interfaces_final_include_no_requisite:
+  file.prepend:
+  - name: /etc/network/interfaces
+  - text: |
+      source /etc/network/interfaces.d/*
+      # Workaround for Upstream-Bug: https://github.com/saltstack/salt/issues/40262
+      source /etc/network/interfaces.u/*
+
 {%- endif %}
 
 {%- endif %}
@@ -365,5 +380,19 @@
   - source: salt://linux/files/60-net-txqueue.rules
   - mode: 755
   - template: jinja
+  - defaults:
+    tap_custom_txqueuelen: {{ network.tap_custom_txqueuelen }}
+
+udev_reload_rules:
+  cmd.run:
+  - name: "/bin/udevadm control --reload-rules"
+  - onchanges:
+    - file: /etc/udev/rules.d/60-net-txqueue.rules
+
+udev_retrigger:
+  cmd.run:
+  - name: "/bin/udevadm trigger --attr-match=subsystem=net"
+  - onchanges:
+    - udev_reload_rules
 
 {%- endif %}
diff --git a/linux/storage/disk.sls b/linux/storage/disk.sls
index 4fc96e5..493b8df 100644
--- a/linux/storage/disk.sls
+++ b/linux/storage/disk.sls
@@ -20,6 +20,9 @@
     - pkg: parted
 
 {% set end_size = 0 -%}
+{% if disk.get('startsector', None) %}
+{% set end_size = disk.get('startsector')|int %}
+{% endif %}
 
 {%- for partition in disk.get('partitions', []) %}
 
@@ -28,7 +31,9 @@
   - name: partition.mkpart
   - device: {{ disk_name }}
   - part_type: primary
+  {%- if partition.type is defined %}
   - fs_type: {{ partition.type }}
+  {%- endif %}
   - start: {{ end_size }}MB
   - end: {{ end_size + partition.size }}MB
   - unless: "blkid {{ disk_name }}{{ loop.index }} {{ disk_name }}p{{ loop.index }}"
diff --git a/linux/storage/mount.sls b/linux/storage/mount.sls
index af77c26..90e9bd1 100644
--- a/linux/storage/mount.sls
+++ b/linux/storage/mount.sls
@@ -10,7 +10,7 @@
 mkfs_{{ mount.device}}:
   cmd.run:
   - name: "mkfs.{{ mount.file_system }} -L {{ name }} {{ mount.device }}"
-  - onlyif: "test `blkid {{ mount.device }} >/dev/null;echo $?` -eq 2"
+  - onlyif: "test `blkid {{ mount.device }} | grep -q TYPE;echo $?` -eq 1"
   - require_in:
     - mount: {{ mount.path }}
   {%- if mount.file_system == 'xfs' %}
diff --git a/linux/system/apt.sls b/linux/system/apt.sls
new file mode 100644
index 0000000..dca8135
--- /dev/null
+++ b/linux/system/apt.sls
@@ -0,0 +1,27 @@
+{%- from "linux/map.jinja" import system with context %}
+{%- if system.enabled %}
+{%- if grains.os_family == 'Debian' %}
+
+{%- if system.repo|length > 0 %}
+include:
+- linux.system.repo
+{%- endif %}
+
+{%- for key, config in system.apt.get('config', {}).iteritems() %}
+
+linux_apt_conf_{{ key }}:
+  file.managed:
+  - name: /etc/apt/apt.conf.d/99{{ key }}-salt
+  - template: jinja
+  - source: salt://linux/files/apt.conf
+  - defaults:
+      config: {{ config|yaml }}
+  {%- if system.repo|length > 0 %}
+  - require_in:
+    - pkg: linux_repo_prereq_pkgs
+  {%- endif %}
+
+{%- endfor %}
+
+{%- endif %}
+{%- endif %}
diff --git a/linux/system/certificate.sls b/linux/system/certificate.sls
index f9f39d4..ffa20c4 100644
--- a/linux/system/certificate.sls
+++ b/linux/system/certificate.sls
@@ -35,7 +35,7 @@
 
 update_certificates:
   cmd.wait:
-  - name: update-ca-certificates
+  - name: {{ system.ca_certs_bin }}
 
 {%- endif %}
 
diff --git a/linux/system/directory.sls b/linux/system/directory.sls
new file mode 100644
index 0000000..f53a870
--- /dev/null
+++ b/linux/system/directory.sls
@@ -0,0 +1,15 @@
+{%- from "linux/map.jinja" import system with context %}
+
+{%- for name, dir in system.directory.iteritems() %}
+
+{{ dir.name|default(name) }}:
+  file.directory:
+    {%- if dir %}
+      {%- for key, value in dir.iteritems() %}
+    - {{ key }}: {{ value }}
+      {%- endfor %}
+    {%- else %}
+    - name: {{ name }}
+    {%- endif %}
+
+{%- endfor %}
diff --git a/linux/system/init.sls b/linux/system/init.sls
index 2f379f4..e17adc5 100644
--- a/linux/system/init.sls
+++ b/linux/system/init.sls
@@ -96,3 +96,9 @@
 {%- if system.systemd is defined %}
 - linux.system.systemd
 {%- endif %}
+{%- if system.directory is defined %}
+- linux.system.directory
+{%- endif %}
+{%- if system.apt is defined and grains.os_family == 'Debian' %}
+- linux.system.apt
+{%- endif %}
diff --git a/linux/system/kernel.sls b/linux/system/kernel.sls
index e3e6bd1..59b7177 100644
--- a/linux/system/kernel.sls
+++ b/linux/system/kernel.sls
@@ -3,11 +3,13 @@
 
 {%- if system.kernel is defined %}
 
-{%- if system.kernel.isolcpu is defined %}
+{%- if system.kernel.isolcpu is defined or system.kernel.elevator is defined %}
 
 include:
   - linux.system.grub
 
+{%- if system.kernel.isolcpu is defined %}
+
 /etc/default/grub.d/90-isolcpu.cfg:
   file.managed:
     - contents: 'GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT isolcpus={{ system.kernel.isolcpu }}"'
@@ -20,6 +22,22 @@
 {%- 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 }}"'
+    - 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:
diff --git a/linux/system/repo.sls b/linux/system/repo.sls
index 813c1e1..1ea921c 100644
--- a/linux/system/repo.sls
+++ b/linux/system/repo.sls
@@ -101,10 +101,12 @@
 
 {%- else %}
 
+{%- if repo.get('enabled', True) %}
+
 linux_repo_{{ name }}:
   pkgrepo.managed:
   {%- if repo.ppa is defined %}
-  - ppa: {{ ppa }}
+  - ppa: {{ repo.ppa }}
   {%- else %}
   - human_name: {{ name }}
   - name: {{ repo.source }}
@@ -138,6 +140,14 @@
   {%- endif %}
   {%- endif %}
 
+{%- else %}
+
+linux_repo_{{ name }}_absent:
+  file.absent:
+    - name: /etc/apt/sources.list.d/{{ name }}.list
+
+{%- endif %}
+
 {%- endif %}
 
 {%- endif %}
diff --git a/linux/system/selinux.sls b/linux/system/selinux.sls
index 5bbd815..245cc8d 100644
--- a/linux/system/selinux.sls
+++ b/linux/system/selinux.sls
@@ -5,22 +5,12 @@
 - linux.system.repo
 
 {%- if grains.os_family == 'RedHat' %}
-
-{%- if system.selinux == 'disabled' %}
-  {%- set mode = 'permissive' %}
-{%- else %}
   {%- set mode = system.selinux %}
-{%- endif %}
-
-selinux_config:
-  cmd.run:
-  - name: "sed -i 's/SELINUX=[a-z][a-z]*$/SELINUX={{ system.selinux }}/' /etc/selinux/config"
-  - unless: grep 'SELINUX={{ system.selinux }}' /etc/selinux/config
-  - require:
-    - pkg: linux_repo_prereq_pkgs
 
 {{ mode }}:
-  selinux.mode
+  selinux.mode:
+    - require:
+      - pkg: linux_repo_prereq_pkgs
 
 {%- endif %}
 
diff --git a/metadata/service/support.yml b/metadata/service/support.yml
index 031dcfb..a59c6f0 100644
--- a/metadata/service/support.yml
+++ b/metadata/service/support.yml
@@ -15,3 +15,5 @@
         enabled: true
       grafana:
         enabled: true
+      fluentd:
+        enabled: true
diff --git a/tests/pillar/network_extended.sls b/tests/pillar/network_extended.sls
index 19587c9..e0e428d 100644
--- a/tests/pillar/network_extended.sls
+++ b/tests/pillar/network_extended.sls
@@ -8,6 +8,7 @@
     hostname: linux
     fqdn: linux.ci.local
     network_manager: false
+    tap_custom_txqueuelen: 10000
     interface:
       eth0:
         enabled: true
diff --git a/tests/pillar/network_openvswitch_dpdk.sls b/tests/pillar/network_openvswitch_dpdk.sls
index fa96cb9..d4e6785 100644
--- a/tests/pillar/network_openvswitch_dpdk.sls
+++ b/tests/pillar/network_openvswitch_dpdk.sls
@@ -16,6 +16,9 @@
       dpdk_socket_mem: "1024"
       dpdk_lcore_mask: "0x400"
       memory_channels: "2"
+      vhost_socket_dir:
+        name: "openvswitch-vhost"
+        path: "/run/openvswitch-vhost"
     interface:
       dpdk0:
         name: enp5s0f1
diff --git a/tests/pillar/system.sls b/tests/pillar/system.sls
index 411323c..06a1bdc 100644
--- a/tests/pillar/system.sls
+++ b/tests/pillar/system.sls
@@ -7,6 +7,9 @@
     environment: prd
     hostname: system.pillar.local
     purge_repos: true
+    directory:
+      /tmp/test:
+        makedirs: true
     apparmor:
       enabled: false
     haveged:
@@ -15,6 +18,7 @@
       default: "linux.ci.local$"
     kernel:
       isolcpu: 1,2,3,4
+      elevator: deadline
     sysfs:
       scheduler:
         block/sda/queue/scheduler: deadline
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index 3f42101..9451611 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -110,7 +110,7 @@
 }
 
 salt_run() {
-    [ -e ${VEN_DIR}/bin/activate ] && source ${VENV_DIR}/bin/activate
+    [ -e ${VENV_DIR}/bin/activate ] && source ${VENV_DIR}/bin/activate
     salt-call ${SALT_OPTS} $*
 }