1""" 2Manage ini files 3================ 4 5:maintainer: <akilesh1597@gmail.com> 6:maturity: new 7:depends: re 8:platform: all 9 10""" 11 12 13from salt.utils.odict import OrderedDict 14 15__virtualname__ = "ini" 16 17 18def __virtual__(): 19 """ 20 Only load if the ini module is available 21 """ 22 return __virtualname__ if "ini.set_option" in __salt__ else False 23 24 25def options_present(name, sections=None, separator="=", strict=False): 26 """ 27 .. code-block:: yaml 28 29 /home/saltminion/api-paste.ini: 30 ini.options_present: 31 - separator: '=' 32 - strict: True 33 - sections: 34 test: 35 testkey: 'testval' 36 secondoption: 'secondvalue' 37 test1: 38 testkey1: 'testval121' 39 40 options present in file and not specified in sections 41 dict will be untouched, unless `strict: True` flag is 42 used 43 44 changes dict will contain the list of changes made 45 """ 46 ret = { 47 "name": name, 48 "changes": {}, 49 "result": True, 50 "comment": "No anomaly detected", 51 } 52 if __opts__["test"]: 53 ret["comment"] = "" 54 # pylint: disable=too-many-nested-blocks 55 try: 56 changes = {} 57 if sections: 58 options = {} 59 for sname, sbody in sections.items(): 60 if not isinstance(sbody, (dict, OrderedDict)): 61 options.update({sname: sbody}) 62 cur_ini = __salt__["ini.get_ini"](name, separator) 63 original_top_level_opts = {} 64 original_sections = {} 65 for key, val in cur_ini.items(): 66 if isinstance(val, (dict, OrderedDict)): 67 original_sections.update({key: val}) 68 else: 69 original_top_level_opts.update({key: val}) 70 if __opts__["test"]: 71 for option in options: 72 if option in original_top_level_opts: 73 if str(original_top_level_opts[option]) == str(options[option]): 74 ret["comment"] += "Unchanged key {}.\n".format(option) 75 else: 76 ret["comment"] += "Changed key {}.\n".format(option) 77 ret["result"] = None 78 else: 79 ret["comment"] += "Changed key {}.\n".format(option) 80 ret["result"] = None 81 else: 82 options_updated = __salt__["ini.set_option"](name, options, separator) 83 changes.update(options_updated) 84 if strict: 85 for opt_to_remove in set(original_top_level_opts).difference(options): 86 if __opts__["test"]: 87 ret["comment"] += "Removed key {}.\n".format(opt_to_remove) 88 ret["result"] = None 89 else: 90 __salt__["ini.remove_option"]( 91 name, None, opt_to_remove, separator 92 ) 93 changes.update( 94 { 95 opt_to_remove: { 96 "before": original_top_level_opts[opt_to_remove], 97 "after": None, 98 } 99 } 100 ) 101 for section_name, section_body in [ 102 (sname, sbody) 103 for sname, sbody in sections.items() 104 if isinstance(sbody, (dict, OrderedDict)) 105 ]: 106 section_descr = " in section " + section_name if section_name else "" 107 changes[section_name] = {} 108 if strict: 109 original = cur_ini.get(section_name, {}) 110 for key_to_remove in set(original.keys()).difference( 111 section_body.keys() 112 ): 113 orig_value = original_sections.get(section_name, {}).get( 114 key_to_remove, "#-#-" 115 ) 116 if __opts__["test"]: 117 ret["comment"] += "Deleted key {}{}.\n".format( 118 key_to_remove, section_descr 119 ) 120 ret["result"] = None 121 else: 122 __salt__["ini.remove_option"]( 123 name, section_name, key_to_remove, separator 124 ) 125 changes[section_name].update({key_to_remove: ""}) 126 changes[section_name].update( 127 {key_to_remove: {"before": orig_value, "after": None}} 128 ) 129 if __opts__["test"]: 130 for option in section_body: 131 if str(section_body[option]) == str( 132 original_sections.get(section_name, {}).get(option, "#-#-") 133 ): 134 ret["comment"] += "Unchanged key {}{}.\n".format( 135 option, section_descr 136 ) 137 else: 138 ret["comment"] += "Changed key {}{}.\n".format( 139 option, section_descr 140 ) 141 ret["result"] = None 142 else: 143 options_updated = __salt__["ini.set_option"]( 144 name, {section_name: section_body}, separator 145 ) 146 if options_updated: 147 changes[section_name].update(options_updated[section_name]) 148 if not changes[section_name]: 149 del changes[section_name] 150 else: 151 if not __opts__["test"]: 152 changes = __salt__["ini.set_option"](name, sections, separator) 153 except (OSError, KeyError) as err: 154 ret["comment"] = "{}".format(err) 155 ret["result"] = False 156 return ret 157 if "error" in changes: 158 ret["result"] = False 159 ret["comment"] = "Errors encountered. {}".format(changes["error"]) 160 ret["changes"] = {} 161 else: 162 for ciname, body in changes.items(): 163 if body: 164 ret["comment"] = "Changes take effect" 165 ret["changes"].update({ciname: changes[ciname]}) 166 return ret 167 168 169def options_absent(name, sections=None, separator="="): 170 """ 171 .. code-block:: yaml 172 173 /home/saltminion/api-paste.ini: 174 ini.options_absent: 175 - separator: '=' 176 - sections: 177 test: 178 - testkey 179 - secondoption 180 test1: 181 - testkey1 182 183 options present in file and not specified in sections 184 dict will be untouched 185 186 changes dict will contain the list of changes made 187 """ 188 ret = { 189 "name": name, 190 "changes": {}, 191 "result": True, 192 "comment": "No anomaly detected", 193 } 194 if __opts__["test"]: 195 ret["result"] = True 196 ret["comment"] = "" 197 for section in sections or {}: 198 section_name = " in section " + section if section else "" 199 try: 200 cur_section = __salt__["ini.get_section"](name, section, separator) 201 except OSError as err: 202 ret["comment"] = "{}".format(err) 203 ret["result"] = False 204 return ret 205 except AttributeError: 206 cur_section = section 207 if isinstance(sections[section], list): 208 for key in sections[section]: 209 cur_value = cur_section.get(key) 210 if not cur_value: 211 ret["comment"] += "Key {}{} does not exist.\n".format( 212 key, section_name 213 ) 214 continue 215 ret["comment"] += "Deleted key {}{}.\n".format(key, section_name) 216 ret["result"] = None 217 else: 218 option = section 219 if not __salt__["ini.get_option"](name, None, option, separator): 220 ret["comment"] += "Key {} does not exist.\n".format(option) 221 continue 222 ret["comment"] += "Deleted key {}.\n".format(option) 223 ret["result"] = None 224 225 if ret["comment"] == "": 226 ret["comment"] = "No changes detected." 227 return ret 228 sections = sections or {} 229 for section, keys in sections.items(): 230 for key in keys: 231 try: 232 current_value = __salt__["ini.remove_option"]( 233 name, section, key, separator 234 ) 235 except OSError as err: 236 ret["comment"] = "{}".format(err) 237 ret["result"] = False 238 return ret 239 if not current_value: 240 continue 241 if section not in ret["changes"]: 242 ret["changes"].update({section: {}}) 243 ret["changes"][section].update({key: current_value}) 244 if not isinstance(sections[section], list): 245 ret["changes"].update({section: current_value}) 246 # break 247 ret["comment"] = "Changes take effect" 248 return ret 249 250 251def sections_present(name, sections=None, separator="="): 252 """ 253 .. code-block:: yaml 254 255 /home/saltminion/api-paste.ini: 256 ini.sections_present: 257 - separator: '=' 258 - sections: 259 - section_one 260 - section_two 261 262 This will only create empty sections. To also create options, use 263 options_present state 264 265 options present in file and not specified in sections will be deleted 266 changes dict will contain the sections that changed 267 """ 268 ret = { 269 "name": name, 270 "changes": {}, 271 "result": True, 272 "comment": "No anomaly detected", 273 } 274 if __opts__["test"]: 275 ret["result"] = True 276 ret["comment"] = "" 277 try: 278 cur_ini = __salt__["ini.get_ini"](name, separator) 279 except OSError as err: 280 ret["result"] = False 281 ret["comment"] = "{}".format(err) 282 return ret 283 for section in sections or {}: 284 if section in cur_ini: 285 ret["comment"] += "Section unchanged {}.\n".format(section) 286 continue 287 else: 288 ret["comment"] += "Created new section {}.\n".format(section) 289 ret["result"] = None 290 if ret["comment"] == "": 291 ret["comment"] = "No changes detected." 292 return ret 293 section_to_update = {} 294 for section_name in sections or []: 295 section_to_update.update({section_name: {}}) 296 try: 297 changes = __salt__["ini.set_option"](name, section_to_update, separator) 298 except OSError as err: 299 ret["result"] = False 300 ret["comment"] = "{}".format(err) 301 return ret 302 if "error" in changes: 303 ret["result"] = False 304 ret["changes"] = "Errors encountered {}".format(changes["error"]) 305 return ret 306 ret["changes"] = changes 307 ret["comment"] = "Changes take effect" 308 return ret 309 310 311def sections_absent(name, sections=None, separator="="): 312 """ 313 .. code-block:: yaml 314 315 /home/saltminion/api-paste.ini: 316 ini.sections_absent: 317 - separator: '=' 318 - sections: 319 - test 320 - test1 321 322 options present in file and not specified in sections will be deleted 323 changes dict will contain the sections that changed 324 """ 325 ret = { 326 "name": name, 327 "changes": {}, 328 "result": True, 329 "comment": "No anomaly detected", 330 } 331 if __opts__["test"]: 332 ret["result"] = True 333 ret["comment"] = "" 334 try: 335 cur_ini = __salt__["ini.get_ini"](name, separator) 336 except OSError as err: 337 ret["result"] = False 338 ret["comment"] = "{}".format(err) 339 return ret 340 for section in sections or []: 341 if section not in cur_ini: 342 ret["comment"] += "Section {} does not exist.\n".format(section) 343 continue 344 ret["comment"] += "Deleted section {}.\n".format(section) 345 ret["result"] = None 346 if ret["comment"] == "": 347 ret["comment"] = "No changes detected." 348 return ret 349 for section in sections or []: 350 try: 351 cur_section = __salt__["ini.remove_section"](name, section, separator) 352 except OSError as err: 353 ret["result"] = False 354 ret["comment"] = "{}".format(err) 355 return ret 356 if not cur_section: 357 continue 358 ret["changes"][section] = cur_section 359 ret["comment"] = "Changes take effect" 360 return ret 361