1""" 2Management of Jenkins 3===================== 4 5.. versionadded:: 2016.3.0 6 7""" 8 9 10import difflib 11import io 12import logging 13 14# Import XML parser 15import xml.etree.ElementTree as ET 16 17import salt.utils.files 18import salt.utils.stringutils 19from salt.exceptions import CommandExecutionError 20 21log = logging.getLogger(__name__) 22 23 24def _elements_equal(e1, e2): 25 if e1.tag != e2.tag: 26 return False 27 if e1.text != e2.text: 28 return False 29 if e1.tail != e2.tail: 30 return False 31 if e1.attrib != e2.attrib: 32 return False 33 if len(e1) != len(e2): 34 return False 35 return all(_elements_equal(c1, c2) for c1, c2 in zip(e1, e2)) 36 37 38def _fail(ret, msg): 39 ret["comment"] = msg 40 ret["result"] = False 41 return ret 42 43 44def present(name, config=None, **kwargs): 45 """ 46 Ensure the job is present in the Jenkins configured jobs 47 48 name 49 The unique name for the Jenkins job 50 51 config 52 The Salt URL for the file to use for configuring the job 53 """ 54 55 ret = { 56 "name": name, 57 "result": True, 58 "changes": {}, 59 "comment": ["Job {} is up to date.".format(name)], 60 } 61 62 if __salt__["jenkins.job_exists"](name): 63 _current_job_config = __salt__["jenkins.get_job_config"](name) 64 buf = io.StringIO(_current_job_config) 65 oldXML = ET.fromstring(buf.read()) 66 67 cached_source_path = __salt__["cp.cache_file"](config, __env__) 68 with salt.utils.files.fopen(cached_source_path) as _fp: 69 newXML = ET.fromstring(salt.utils.stringutils.to_unicode(_fp.read())) 70 if not _elements_equal(oldXML, newXML): 71 diff = difflib.unified_diff( 72 ET.tostringlist(oldXML, encoding="utf8", method="xml"), 73 ET.tostringlist(newXML, encoding="utf8", method="xml"), 74 lineterm="", 75 ) 76 try: 77 __salt__["jenkins.update_job"](name, config, __env__) 78 except CommandExecutionError as exc: 79 return _fail(ret, exc.strerror) 80 else: 81 ret["changes"] = "".join(diff) 82 ret["comment"].append("Job '{}' updated.".format(name)) 83 84 else: 85 cached_source_path = __salt__["cp.cache_file"](config, __env__) 86 with salt.utils.files.fopen(cached_source_path) as _fp: 87 new_config_xml = salt.utils.stringutils.to_unicode(_fp.read()) 88 89 try: 90 __salt__["jenkins.create_job"](name, config, __env__) 91 except CommandExecutionError as exc: 92 return _fail(ret, exc.strerror) 93 94 buf = io.StringIO(new_config_xml) 95 diff = difflib.unified_diff("", buf.readlines(), lineterm="") 96 ret["changes"][name] = "".join(diff) 97 ret["comment"].append("Job '{}' added.".format(name)) 98 99 ret["comment"] = "\n".join(ret["comment"]) 100 return ret 101 102 103def absent(name, **kwargs): 104 """ 105 Ensure the job is absent from the Jenkins configured jobs 106 107 name 108 The name of the Jenkins job to remove 109 """ 110 ret = {"name": name, "result": True, "changes": {}, "comment": []} 111 112 if __salt__["jenkins.job_exists"](name): 113 try: 114 __salt__["jenkins.delete_job"](name) 115 except CommandExecutionError as exc: 116 return _fail(ret, exc.strerror) 117 else: 118 ret["comment"] = "Job '{}' deleted.".format(name) 119 else: 120 ret["comment"] = "Job '{}' already absent.".format(name) 121 return ret 122