­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ """ Mounting of filesystems ======================= Mount any type of mountable filesystem with the mounted function: .. code-block:: yaml /mnt/sdb: mount.mounted: - device: /dev/sdb1 - fstype: ext4 - mkmnt: True - opts: - defaults /srv/bigdata: mount.mounted: - device: UUID=066e0200-2867-4ebe-b9e6-f30026ca2314 - fstype: xfs - opts: nobootwait,noatime,nodiratime,nobarrier,logbufs=8 - dump: 0 - pass_num: 2 - persist: True - mkmnt: True /var/lib/bigdata: mount.mounted: - device: /srv/bigdata - fstype: none - opts: bind - dump: 0 - pass_num: 0 - persist: True - mkmnt: True """ import logging import os.path import re log = logging.getLogger(__name__) def _size_convert(_re_size): converted_size = int(_re_size.group("size_value")) if _re_size.group("size_unit") == "m": converted_size = int(converted_size) * 1024 if _re_size.group("size_unit") == "g": converted_size = int(converted_size) * 1024 * 1024 return converted_size def mounted( name, device, fstype, mkmnt=False, opts="defaults", dump=0, pass_num=0, config="/etc/fstab", persist=True, mount=True, user=None, match_on="auto", device_name_regex=None, extra_mount_invisible_options=None, extra_mount_invisible_keys=None, extra_mount_ignore_fs_keys=None, extra_mount_translate_options=None, hidden_opts=None, bind_mount_copy_active_opts=True, **kwargs, ): """ Verify that a device is mounted name The path to the location where the device is to be mounted device The device name, typically the device node, such as ``/dev/sdb1`` or ``UUID=066e0200-2867-4ebe-b9e6-f30026ca2314`` or ``LABEL=DATA`` fstype The filesystem type, this will be ``xfs``, ``ext2/3/4`` in the case of classic filesystems, ``fuse`` in the case of fuse mounts, and ``nfs`` in the case of nfs mounts mkmnt If the mount point is not present then the state will fail, set ``mkmnt: True`` to create the mount point if it is otherwise not present opts A list object of options or a comma delimited list dump The dump value to be passed into the fstab, Default is ``0`` pass_num The pass value to be passed into the fstab, Default is ``0`` config Set an alternative location for the fstab, Default is ``/etc/fstab`` persist Set if the mount should be saved in the fstab, Default is ``True`` mount Set if the mount should be mounted immediately, Default is ``True`` user The account used to execute the mount; this defaults to the user salt is running as on the minion match_on A name or list of fstab properties on which this state should be applied. Default is ``auto``, a special value indicating to guess based on fstype. In general, ``auto`` matches on name for recognized special devices and device otherwise. device_name_regex A list of device exact names or regular expressions which should not force a remount. For example, glusterfs may be mounted with a comma-separated list of servers in fstab, but the /proc/self/mountinfo will show only the first available server. .. code-block:: jinja {% set glusterfs_ip_list = ['10.0.0.1', '10.0.0.2', '10.0.0.3'] %} mount glusterfs volume: mount.mounted: - name: /mnt/glusterfs_mount_point - device: {{ glusterfs_ip_list|join(',') }}:/volume_name - fstype: glusterfs - opts: _netdev,rw,defaults,direct-io-mode=disable - mkmnt: True - persist: True - dump: 0 - pass_num: 0 - device_name_regex: - ({{ glusterfs_ip_list|join('|') }}):/volume_name .. versionadded:: 2016.11.0 extra_mount_invisible_options A list of extra options that are not visible through the ``/proc/self/mountinfo`` interface. If a option is not visible through this interface it will always remount the device. This option extends the builtin ``mount_invisible_options`` list. extra_mount_invisible_keys A list of extra key options that are not visible through the ``/proc/self/mountinfo`` interface. If a key option is not visible through this interface it will always remount the device. This option extends the builtin ``mount_invisible_keys`` list. A good example for a key option is the password option:: password=badsecret extra_mount_ignore_fs_keys A dict of filesystem options which should not force a remount. This will update the internal dictionary. The dict should look like this:: { 'ramfs': ['size'] } extra_mount_translate_options A dict of mount options that gets translated when mounted. To prevent a remount add additional options to the default dictionary. This will update the internal dictionary. The dictionary should look like this:: { 'tcp': 'proto=tcp', 'udp': 'proto=udp' } hidden_opts A list of mount options that will be ignored when considering a remount as part of the state application .. versionadded:: 2015.8.2 bind_mount_copy_active_opts If set to ``False``, this option disables the default behavior of copying the options from the bind mount if it was found to be active. .. versionadded:: 3006.0 """ ret = {"name": name, "changes": {}, "result": True, "comment": ""} update_mount_cache = False if not name: ret["result"] = False ret["comment"] = "Must provide name to mount.mounted" return ret if not device: ret["result"] = False ret["comment"] = "Must provide device to mount.mounted" return ret if not fstype: ret["result"] = False ret["comment"] = "Must provide fstype to mount.mounted" return ret if device_name_regex is None: device_name_regex = [] # Defaults is not a valid option on Mac OS if __grains__["os"] in ["MacOS", "Darwin"] and opts == "defaults": opts = "noowners" # Defaults is not a valid option on AIX if __grains__["os"] in ["AIX"]: if opts == "defaults": opts = "" # Make sure that opts is correct, it can be a list or a comma delimited # string if isinstance(opts, str): opts = opts.split(",") if isinstance(hidden_opts, str): hidden_opts = hidden_opts.split(",") # remove possible trailing slash if not name == "/": name = name.rstrip("/") device_list = [] # Get the active data active = __salt__["mount.active"](extended=True) real_name = os.path.realpath(name) # real_name for comparisons to the active mount list comp_real_name = real_name.replace(" ", "\\040") if device.startswith("/"): if "bind" in opts and comp_real_name in active: _device = device.replace(" ", "\\040") if active[comp_real_name]["device"].startswith("/"): # Find the device that the bind really points at. while True: if _device in active: _real_device = active[_device]["device"] if bind_mount_copy_active_opts: opts = sorted( set( opts + active[_device]["opts"] + active[_device]["superopts"] ) ) active[comp_real_name]["opts"].append("bind") break _device = os.path.dirname(_device.replace("\\040", " ")) real_device = _real_device else: # Remote file systems act differently. if _device in active: if bind_mount_copy_active_opts: opts = sorted( set( opts + active[_device]["opts"] + active[_device]["superopts"] ) ) active[comp_real_name]["opts"].append("bind") real_device = active[comp_real_name]["device"] else: real_device = os.path.realpath(device) elif device.upper().startswith("UUID="): real_device = device.split("=")[1].strip('"').lower() elif device.upper().startswith("LABEL="): _label = device.split("=")[1] cmd = f"blkid -t LABEL={_label}" res = __salt__["cmd.run_all"](f"{cmd}") if res["retcode"] > 0: ret["comment"] = f"Unable to find device with label {_label}." ret["result"] = False return ret else: # output is a list of entries like this: # /dev/sda: LABEL="