Jakub Josef | e380798 | 2016-12-15 11:54:51 +0100 | [diff] [blame] | 1 | # -*- coding: utf-8 -*- |
| 2 | |
| 3 | # Import Python libs |
| 4 | from __future__ import absolute_import |
| 5 | import difflib |
| 6 | import logging |
| 7 | |
| 8 | # Import Salt libs |
| 9 | import salt.ext.six as six |
| 10 | import salt.utils |
| 11 | |
| 12 | # Import XML parser |
| 13 | import xml.etree.ElementTree as ET |
| 14 | |
| 15 | log = logging.getLogger(__name__) |
| 16 | |
| 17 | |
Ilya Kharin | 3d8bffe | 2017-06-22 17:40:31 +0400 | [diff] [blame] | 18 | def __virtual__(): |
| 19 | ''' |
| 20 | Only load if jenkins_common module exist. |
| 21 | ''' |
| 22 | if 'jenkins_common.call_groovy_script' not in __salt__: |
| 23 | return ( |
| 24 | False, |
| 25 | 'The jenkins_job state module cannot be loaded: ' |
| 26 | 'jenkins_common not found') |
| 27 | return True |
| 28 | |
| 29 | |
Jakub Josef | e380798 | 2016-12-15 11:54:51 +0100 | [diff] [blame] | 30 | def _elements_equal(e1, e2): |
| 31 | if e1.tag != e2.tag: |
| 32 | return False |
| 33 | if e1.text != e2.text: |
| 34 | return False |
| 35 | if e1.tail != e2.tail: |
| 36 | return False |
| 37 | if e1.attrib != e2.attrib: |
| 38 | return False |
| 39 | if len(e1) != len(e2): |
| 40 | return False |
| 41 | return all(_elements_equal(c1, c2) for c1, c2 in zip(e1, e2)) |
| 42 | |
| 43 | |
| 44 | def present(name, |
| 45 | config=None, |
| 46 | **kwargs): |
| 47 | ''' |
| 48 | Ensure the job is present in the Jenkins |
| 49 | configured jobs |
| 50 | name |
| 51 | The unique name for the Jenkins job |
| 52 | config |
| 53 | The Salt URL for the file to use for |
| 54 | configuring the job. |
| 55 | ''' |
Jakub Josef | 2a7739b | 2017-01-24 18:33:44 +0100 | [diff] [blame] | 56 | test = __opts__['test'] |
Jakub Josef | e380798 | 2016-12-15 11:54:51 +0100 | [diff] [blame] | 57 | ret = {'name': name, |
| 58 | 'result': True, |
| 59 | 'changes': {}, |
| 60 | 'comment': ['Job {0} is up to date.'.format(name)]} |
Jakub Josef | 2a7739b | 2017-01-24 18:33:44 +0100 | [diff] [blame] | 61 | if test: |
| 62 | status = 'CREATED' |
| 63 | ret['changes'][name] = status |
| 64 | ret['comment'] = 'Job %s %s' % (name, status.lower()) |
Jakub Josef | e380798 | 2016-12-15 11:54:51 +0100 | [diff] [blame] | 65 | else: |
Jakub Josef | 2a7739b | 2017-01-24 18:33:44 +0100 | [diff] [blame] | 66 | _job_exists = __salt__['jenkins.job_exists'](name) |
| 67 | if _job_exists: |
| 68 | _current_job_config = __salt__['jenkins.get_job_config'](name) |
| 69 | buf = six.moves.StringIO(_current_job_config) |
| 70 | oldXML = ET.fromstring(buf.read()) |
Jakub Josef | e380798 | 2016-12-15 11:54:51 +0100 | [diff] [blame] | 71 | |
Jakub Josef | 2a7739b | 2017-01-24 18:33:44 +0100 | [diff] [blame] | 72 | cached_source_path = __salt__['cp.cache_file'](config, __env__) |
| 73 | with salt.utils.fopen(cached_source_path) as _fp: |
| 74 | newXML = ET.fromstring(_fp.read()) |
| 75 | if not _elements_equal(oldXML, newXML): |
| 76 | diff = difflib.unified_diff( |
| 77 | ET.tostringlist(oldXML, encoding='utf8', method='xml'), |
| 78 | ET.tostringlist(newXML, encoding='utf8', method='xml'), lineterm='') |
| 79 | __salt__['jenkins.update_job'](name, config, __env__) |
Jakub Josef | 654a148 | 2017-01-26 17:41:16 +0100 | [diff] [blame] | 80 | ret['changes'][name] = ''.join(diff) |
Jakub Josef | 2a7739b | 2017-01-24 18:33:44 +0100 | [diff] [blame] | 81 | ret['comment'].append('Job {0} updated.'.format(name)) |
Jakub Josef | e380798 | 2016-12-15 11:54:51 +0100 | [diff] [blame] | 82 | |
Jakub Josef | 2a7739b | 2017-01-24 18:33:44 +0100 | [diff] [blame] | 83 | else: |
| 84 | cached_source_path = __salt__['cp.cache_file'](config, __env__) |
| 85 | with salt.utils.fopen(cached_source_path) as _fp: |
| 86 | new_config_xml = _fp.read() |
Jakub Josef | e380798 | 2016-12-15 11:54:51 +0100 | [diff] [blame] | 87 | |
Jakub Josef | 2a7739b | 2017-01-24 18:33:44 +0100 | [diff] [blame] | 88 | __salt__['jenkins.create_job'](name, config, __env__) |
Jakub Josef | e380798 | 2016-12-15 11:54:51 +0100 | [diff] [blame] | 89 | |
Jakub Josef | 2a7739b | 2017-01-24 18:33:44 +0100 | [diff] [blame] | 90 | buf = six.moves.StringIO(new_config_xml) |
Jakub Josef | 654a148 | 2017-01-26 17:41:16 +0100 | [diff] [blame] | 91 | diff = difflib.unified_diff('', buf.readlines(), lineterm='') |
| 92 | ret['changes'][name] = ''.join(diff) |
Jakub Josef | 2a7739b | 2017-01-24 18:33:44 +0100 | [diff] [blame] | 93 | ret['comment'].append('Job {0} added.'.format(name)) |
| 94 | |
| 95 | ret['comment'] = '\n'.join(ret['comment']) |
Jakub Josef | e380798 | 2016-12-15 11:54:51 +0100 | [diff] [blame] | 96 | return ret |
| 97 | |
| 98 | |
| 99 | def absent(name, |
| 100 | **kwargs): |
| 101 | ''' |
| 102 | Ensure the job is present in the Jenkins |
| 103 | configured jobs |
| 104 | |
| 105 | name |
| 106 | The name of the Jenkins job to remove. |
| 107 | |
| 108 | ''' |
Jakub Josef | 2a7739b | 2017-01-24 18:33:44 +0100 | [diff] [blame] | 109 | test = __opts__['test'] |
Jakub Josef | e380798 | 2016-12-15 11:54:51 +0100 | [diff] [blame] | 110 | ret = {'name': name, |
| 111 | 'result': True, |
| 112 | 'changes': {}, |
| 113 | 'comment': []} |
Jakub Josef | 2a7739b | 2017-01-24 18:33:44 +0100 | [diff] [blame] | 114 | if test: |
| 115 | status = 'DELETED' |
| 116 | ret['changes'][name] = status |
| 117 | ret['comment'] = 'Node %s %s' % (name, status.lower()) |
Jakub Josef | e380798 | 2016-12-15 11:54:51 +0100 | [diff] [blame] | 118 | else: |
Jakub Josef | 2a7739b | 2017-01-24 18:33:44 +0100 | [diff] [blame] | 119 | _job_exists = __salt__['jenkins.job_exists'](name) |
| 120 | |
| 121 | if _job_exists: |
| 122 | __salt__['jenkins.delete_job'](name) |
| 123 | ret['comment'] = 'Job {0} deleted.'.format(name) |
| 124 | else: |
| 125 | ret['comment'] = 'Job {0} already absent.'.format(name) |
| 126 | return ret |
| 127 | |
| 128 | |
| 129 | def cleanup(name, jobs, **kwargs): |
| 130 | ''' |
| 131 | Perform a cleanup - uninstall any installed job absents in given jobs list |
| 132 | |
| 133 | name |
| 134 | The name of the Jenkins job to remove. |
| 135 | jobs |
| 136 | List of jobs which may NOT be uninstalled |
| 137 | |
| 138 | ''' |
| 139 | test = __opts__['test'] |
| 140 | ret = {'name': name, |
| 141 | 'result': True, |
| 142 | 'changes': {}, |
| 143 | 'comment': "Cleanup not necessary"} |
| 144 | list_jobs_groovy = """\ |
| 145 | print(Jenkins.instance.items.collect{{it -> it.name}}) |
| 146 | """ |
| 147 | deleted_jobs = [] |
| 148 | if test: |
| 149 | status = 'CLEANED' |
| 150 | ret['changes'][name] = status |
| 151 | ret['comment'] = 'Jobs %s' % status.lower() |
| 152 | else: |
| 153 | call_result = __salt__['jenkins_common.call_groovy_script'](list_jobs_groovy,{}) |
| 154 | if call_result["code"] == 200: |
| 155 | existing_jobs = call_result["msg"] |
| 156 | if existing_jobs: |
| 157 | for job in existing_jobs[1:-1].split(","): |
| 158 | if job: |
| 159 | job = job.strip() |
| 160 | if job not in jobs: |
| 161 | __salt__['jenkins.delete_job'](job) |
| 162 | deleted_jobs.append(job) |
| 163 | else: |
| 164 | log.error("Cannot get existing jobs list from Jenkins") |
| 165 | if len(deleted_jobs) > 0: |
| 166 | for del_job in deleted_jobs: |
| 167 | ret['changes'][del_job] = "removed" |
| 168 | ret['comment'] = 'Jobs {} deleted.'.format(deleted_jobs) |
| 169 | else: |
| 170 | status = 'FAILED' |
| 171 | log.error( |
| 172 | "Jenkins jobs API call failure: %s", call_result["msg"]) |
| 173 | ret['comment'] = 'Jenkins jobs API call failure: %s' % ( |
| 174 | call_result["msg"]) |
Jakub Josef | e380798 | 2016-12-15 11:54:51 +0100 | [diff] [blame] | 175 | return ret |