1""" 2Managing software RAID with mdadm 3================================== 4 5:depends: mdadm 6 7A state module for creating or destroying software RAID devices. 8 9.. code-block:: yaml 10 11 /dev/md0: 12 raid.present: 13 - level: 5 14 - devices: 15 - /dev/xvdd 16 - /dev/xvde 17 - /dev/xvdf 18 - chunk: 256 19 - run: True 20""" 21 22 23import logging 24 25import salt.utils.path 26 27# Set up logger 28log = logging.getLogger(__name__) 29 30# Define the module's virtual name 31__virtualname__ = "raid" 32 33 34def __virtual__(): 35 """ 36 mdadm provides raid functions for Linux 37 """ 38 if __grains__["kernel"] != "Linux": 39 return (False, "Only supported on Linux") 40 if not salt.utils.path.which("mdadm"): 41 return (False, "Unable to locate command: mdadm") 42 return __virtualname__ 43 44 45def present(name, level, devices, **kwargs): 46 """ 47 Verify that the raid is present 48 49 .. versionchanged:: 2014.7.0 50 51 name 52 The name of raid device to be created 53 54 level 55 The RAID level to use when creating the raid. 56 57 devices 58 A list of devices used to build the array. 59 60 kwargs 61 Optional arguments to be passed to mdadm. 62 63 Example: 64 65 .. code-block:: yaml 66 67 /dev/md0: 68 raid.present: 69 - level: 5 70 - devices: 71 - /dev/xvdd 72 - /dev/xvde 73 - /dev/xvdf 74 - chunk: 256 75 - run: True 76 """ 77 ret = {"changes": {}, "comment": "", "name": name, "result": True} 78 79 # Device exists 80 raids = __salt__["raid.list"]() 81 present = raids.get(name) 82 83 # Decide whether to create or assemble 84 missing = [] 85 uuid_dict = {} 86 new_devices = [] 87 88 for dev in devices: 89 if dev == "missing" or not __salt__["file.access"](dev, "f"): 90 missing.append(dev) 91 continue 92 superblock = __salt__["raid.examine"](dev, quiet=True) 93 94 if "MD_UUID" in superblock: 95 uuid = superblock["MD_UUID"] 96 if uuid not in uuid_dict: 97 uuid_dict[uuid] = [] 98 uuid_dict[uuid].append(dev) 99 else: 100 new_devices.append(dev) 101 102 if len(uuid_dict) > 1: 103 ret[ 104 "comment" 105 ] = "Devices are a mix of RAID constituents with multiple MD_UUIDs: {}.".format( 106 sorted(uuid_dict) 107 ) 108 ret["result"] = False 109 return ret 110 elif len(uuid_dict) == 1: 111 uuid = next(iter(uuid_dict)) 112 if present and present["uuid"] != uuid: 113 ret[ 114 "comment" 115 ] = "Devices MD_UUIDs: {} differs from present RAID uuid {}.".format( 116 uuid, present["uuid"] 117 ) 118 ret["result"] = False 119 return ret 120 121 devices_with_superblock = uuid_dict[uuid] 122 else: 123 devices_with_superblock = [] 124 125 if present: 126 do_assemble = False 127 do_create = False 128 elif len(devices_with_superblock) > 0: 129 do_assemble = True 130 do_create = False 131 verb = "assembled" 132 else: 133 if len(new_devices) == 0: 134 ret["comment"] = "All devices are missing: {}.".format(missing) 135 ret["result"] = False 136 return ret 137 do_assemble = False 138 do_create = True 139 verb = "created" 140 141 # If running with test use the test_mode with create or assemble 142 if __opts__["test"]: 143 if do_assemble: 144 res = __salt__["raid.assemble"]( 145 name, devices_with_superblock, test_mode=True, **kwargs 146 ) 147 elif do_create: 148 res = __salt__["raid.create"]( 149 name, 150 level, 151 new_devices + ["missing"] * len(missing), 152 test_mode=True, 153 **kwargs 154 ) 155 156 if present: 157 ret["comment"] = "Raid {} already present.".format(name) 158 159 if do_assemble or do_create: 160 ret["comment"] = "Raid will be {} with: {}".format(verb, res) 161 ret["result"] = None 162 163 if (do_assemble or present) and len(new_devices) > 0: 164 ret["comment"] += " New devices will be added: {}".format(new_devices) 165 ret["result"] = None 166 167 if len(missing) > 0: 168 ret["comment"] += " Missing devices: {}".format(missing) 169 170 return ret 171 172 # Attempt to create or assemble the array 173 if do_assemble: 174 __salt__["raid.assemble"](name, devices_with_superblock, **kwargs) 175 elif do_create: 176 __salt__["raid.create"]( 177 name, level, new_devices + ["missing"] * len(missing), **kwargs 178 ) 179 180 if not present: 181 raids = __salt__["raid.list"]() 182 changes = raids.get(name) 183 if changes: 184 ret["comment"] = "Raid {} {}.".format(name, verb) 185 ret["changes"] = changes 186 # Saving config 187 __salt__["raid.save_config"]() 188 else: 189 ret["comment"] = "Raid {} failed to be {}.".format(name, verb) 190 ret["result"] = False 191 else: 192 ret["comment"] = "Raid {} already present.".format(name) 193 194 if (do_assemble or present) and len(new_devices) > 0 and ret["result"]: 195 for d in new_devices: 196 res = __salt__["raid.add"](name, d) 197 if not res: 198 ret["comment"] += " Unable to add {} to {}.\n".format(d, name) 199 ret["result"] = False 200 else: 201 ret["comment"] += " Added new device {} to {}.\n".format(d, name) 202 if ret["result"]: 203 ret["changes"]["added"] = new_devices 204 205 if len(missing) > 0: 206 ret["comment"] += " Missing devices: {}".format(missing) 207 208 return ret 209 210 211def absent(name): 212 """ 213 Verify that the raid is absent 214 215 name 216 The name of raid device to be destroyed 217 218 .. code-block:: yaml 219 220 /dev/md0: 221 raid: 222 - absent 223 """ 224 ret = {"changes": {}, "comment": "", "name": name, "result": True} 225 226 # Raid does not exist 227 if name not in __salt__["raid.list"](): 228 ret["comment"] = "Raid {} already absent".format(name) 229 return ret 230 elif __opts__["test"]: 231 ret["comment"] = "Raid {} is set to be destroyed".format(name) 232 ret["result"] = None 233 return ret 234 else: 235 # Attempt to destroy raid 236 ret["result"] = __salt__["raid.destroy"](name) 237 238 if ret["result"]: 239 ret["comment"] = "Raid {} has been destroyed".format(name) 240 else: 241 ret["comment"] = "Raid {} failed to be destroyed".format(name) 242 return ret 243