blob: 74be81cab0c334df60475a8bf0f33da25135090e [file] [log] [blame]
Jakub Josefe3807982016-12-15 11:54:51 +01001# -*- coding: utf-8 -*-
2
3# Import Python libs
4from __future__ import absolute_import
5import difflib
6import logging
7
8# Import Salt libs
9import salt.ext.six as six
10import salt.utils
11
12# Import XML parser
13import xml.etree.ElementTree as ET
14
15log = logging.getLogger(__name__)
16
17
Ilya Kharin3d8bffe2017-06-22 17:40:31 +040018def __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 Josefe3807982016-12-15 11:54:51 +010030def _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
44def 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 Josef2a7739b2017-01-24 18:33:44 +010056 test = __opts__['test']
Jakub Josefe3807982016-12-15 11:54:51 +010057 ret = {'name': name,
58 'result': True,
59 'changes': {},
60 'comment': ['Job {0} is up to date.'.format(name)]}
Jakub Josef2a7739b2017-01-24 18:33:44 +010061 if test:
62 status = 'CREATED'
63 ret['changes'][name] = status
64 ret['comment'] = 'Job %s %s' % (name, status.lower())
Jakub Josefe3807982016-12-15 11:54:51 +010065 else:
Jakub Josef2a7739b2017-01-24 18:33:44 +010066 _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 Josefe3807982016-12-15 11:54:51 +010071
Jakub Josef2a7739b2017-01-24 18:33:44 +010072 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 Josef654a1482017-01-26 17:41:16 +010080 ret['changes'][name] = ''.join(diff)
Jakub Josef2a7739b2017-01-24 18:33:44 +010081 ret['comment'].append('Job {0} updated.'.format(name))
Jakub Josefe3807982016-12-15 11:54:51 +010082
Jakub Josef2a7739b2017-01-24 18:33:44 +010083 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 Josefe3807982016-12-15 11:54:51 +010087
Jakub Josef2a7739b2017-01-24 18:33:44 +010088 __salt__['jenkins.create_job'](name, config, __env__)
Jakub Josefe3807982016-12-15 11:54:51 +010089
Jakub Josef2a7739b2017-01-24 18:33:44 +010090 buf = six.moves.StringIO(new_config_xml)
Jakub Josef654a1482017-01-26 17:41:16 +010091 diff = difflib.unified_diff('', buf.readlines(), lineterm='')
92 ret['changes'][name] = ''.join(diff)
Jakub Josef2a7739b2017-01-24 18:33:44 +010093 ret['comment'].append('Job {0} added.'.format(name))
94
95 ret['comment'] = '\n'.join(ret['comment'])
Jakub Josefe3807982016-12-15 11:54:51 +010096 return ret
97
98
99def 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 Josef2a7739b2017-01-24 18:33:44 +0100109 test = __opts__['test']
Jakub Josefe3807982016-12-15 11:54:51 +0100110 ret = {'name': name,
111 'result': True,
112 'changes': {},
113 'comment': []}
Jakub Josef2a7739b2017-01-24 18:33:44 +0100114 if test:
115 status = 'DELETED'
116 ret['changes'][name] = status
117 ret['comment'] = 'Node %s %s' % (name, status.lower())
Jakub Josefe3807982016-12-15 11:54:51 +0100118 else:
Jakub Josef2a7739b2017-01-24 18:33:44 +0100119 _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
129def 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 Josefe3807982016-12-15 11:54:51 +0100175 return ret