Add linux.storage.loopback state

This state allows to configure loopback device(s). This isn't meant to
be used in production but offers a cheap way to test Cinder with LVM
volumes.
diff --git a/README.rst b/README.rst
index b2cf09f..1c7d79b 100644
--- a/README.rst
+++ b/README.rst
@@ -693,6 +693,17 @@
           multipath:
             enabled: false
 
+Linux with local loopback device
+
+.. code-block:: yaml
+
+    linux:
+      storage:
+        loopback:
+          disk1:
+            file: /srv/disk1
+            size: 50G
+
 External config generation
 --------------------------
 
diff --git a/linux/files/setup-loopback-device.systemd b/linux/files/setup-loopback-device.systemd
new file mode 100644
index 0000000..1e2245a
--- /dev/null
+++ b/linux/files/setup-loopback-device.systemd
@@ -0,0 +1,14 @@
+[Unit]
+Description=Setup {{ device_name }} device
+DefaultDependencies=no
+After=systemd-udev-settle.service
+Before=lvm2-activation-early.service
+Wants=systemd-udev-settle.service
+
+[Service]
+{# The command is prefixed with '-' to consider it a success if the loopback device is already setup #}
+ExecStart=-/sbin/losetup {{ device_name }} {{ file }}
+Type=oneshot
+
+[Install]
+WantedBy=local-fs.target
diff --git a/linux/files/setup-loopback-device.upstart b/linux/files/setup-loopback-device.upstart
new file mode 100644
index 0000000..a1acd24
--- /dev/null
+++ b/linux/files/setup-loopback-device.upstart
@@ -0,0 +1,12 @@
+description "Setup {{ device_name }} device"
+
+start on filesystem
+task
+
+pre-start script
+  if /sbin/losetup {{ device_name }}; then
+    stop ; exit 0
+  fi
+end script
+
+exec losetup {{ device_name }} {{ file }}
diff --git a/linux/map.jinja b/linux/map.jinja
index 3c17e51..c2cfc7c 100644
--- a/linux/map.jinja
+++ b/linux/map.jinja
@@ -126,6 +126,7 @@
         'mount': {},
         'swap': {},
         'lvm': {},
+        'loopback': {},
         'multipath': {
              'enabled': False,
              'pkgs': ['multipath-tools', 'multipath-tools-boot'],
@@ -136,6 +137,7 @@
         'mount': {},
         'swap': {},
         'lvm': {},
+        'loopback': {},
         'multipath': {
              'enabled': False,
              'pkgs': ['multipath-tools', 'multipath-tools-boot'],
@@ -147,6 +149,7 @@
         'mount': {},
         'swap': {},
         'lvm': {},
+        'loopback': {},
         'multipath': {
              'enabled': False,
              'pkgs': [],
diff --git a/linux/storage/init.sls b/linux/storage/init.sls
index 102d71a..19d2ade 100644
--- a/linux/storage/init.sls
+++ b/linux/storage/init.sls
@@ -1,6 +1,9 @@
 {%- from "linux/map.jinja" import storage with context %}
-{%- if storage.mount|length > 0 or storage.swap|length > 0 or storage.multipath.enabled or storage.lvm|length > 0 %}
+{%- if storage.mount|length > 0 or storage.swap|length > 0 or storage.multipath.enabled or storage.lvm|length > 0 or storage.loopback|length > 0 %}
 include:
+{%- if storage.loopback|length > 0 %}
+- linux.storage.loopback
+{%- endif %}
 {%- if storage.mount|length > 0 %}
 - linux.storage.mount
 {%- endif %}
diff --git a/linux/storage/loopback.sls b/linux/storage/loopback.sls
new file mode 100644
index 0000000..34008e1
--- /dev/null
+++ b/linux/storage/loopback.sls
@@ -0,0 +1,44 @@
+{%- from "linux/map.jinja" import storage with context %}
+
+{%- if storage.get('enabled', False) %}
+
+{%- for device, loopback in storage.loopback|dictsort %}
+
+{%- if loopback.get('enabled', True) %}
+
+{{ salt['file.dirname'](loopback.file) }}:
+  file.directory:
+  - makedirs: true
+  - require_in:
+    - file: {{ loopback.file }}
+
+{{ loopback.file }}:
+  cmd.run:
+  - name: "truncate --size {{ loopback.size|default('1G') }} {{ loopback.file }}"
+  - creates: {{ loopback.file }}
+
+loopback_{{ device }}_init_script:
+  file.managed:
+{%- if grains.get('init', None) == 'upstart' %}
+  - name: /etc/init/setup-loopback-{{ device }}.conf
+  - source: salt://linux/files/setup-loopback-device.upstart
+{%- else %}
+  - name: /etc/systemd/system/setup-loopback-{{ device }}.service
+  - source: salt://linux/files/setup-loopback-device.systemd
+{%- endif %}
+  - template: jinja
+  - defaults:
+    file: {{ loopback.file }}
+    device_name: "/dev/loop{{ loop.index0 }}"
+
+setup-loopback-{{ device }}:
+  service.running:
+  - enable: true
+  - require:
+    - cmd: {{ loopback.file }}
+    - file: loopback_{{ device }}_init_script
+{%- endif %}
+
+{%- endfor %}
+
+{%- endif %}