blob: 413908d1cc242e895c22f71c810576682989eae3 [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
chnyda90f133f2017-08-02 10:46:13 +020014import hashlib
15
16# Jenkins
17try:
18 import jenkins
19 HAS_JENKINS = True
20except ImportError:
21 HAS_JENKINS = False
Jakub Josefe3807982016-12-15 11:54:51 +010022
23log = logging.getLogger(__name__)
24
25
Ilya Kharin3d8bffe2017-06-22 17:40:31 +040026def __virtual__():
27 '''
28 Only load if jenkins_common module exist.
29 '''
chnyda90f133f2017-08-02 10:46:13 +020030 if HAS_JENKINS and 'jenkins_common.call_groovy_script' not in __salt__:
Ilya Kharin3d8bffe2017-06-22 17:40:31 +040031 return (
32 False,
33 'The jenkins_job state module cannot be loaded: '
34 'jenkins_common not found')
35 return True
36
37
Jakub Josefe3807982016-12-15 11:54:51 +010038def _elements_equal(e1, e2):
chnyda90f133f2017-08-02 10:46:13 +020039 return hashlib.md5(e1).hexdigest() == hashlib.md5(e2).hexdigest()
Jakub Josefe3807982016-12-15 11:54:51 +010040
41
42def present(name,
43 config=None,
44 **kwargs):
45 '''
46 Ensure the job is present in the Jenkins
47 configured jobs
48 name
49 The unique name for the Jenkins job
50 config
51 The Salt URL for the file to use for
52 configuring the job.
53 '''
Jakub Josef2a7739b2017-01-24 18:33:44 +010054 test = __opts__['test']
Jakub Josefe3807982016-12-15 11:54:51 +010055 ret = {'name': name,
56 'result': True,
57 'changes': {},
58 'comment': ['Job {0} is up to date.'.format(name)]}
Jakub Josef2a7739b2017-01-24 18:33:44 +010059 if test:
60 status = 'CREATED'
61 ret['changes'][name] = status
62 ret['comment'] = 'Job %s %s' % (name, status.lower())
Jakub Josefe3807982016-12-15 11:54:51 +010063 else:
chnyda90f133f2017-08-02 10:46:13 +020064 _current_job_config = ''
65 _job_exists = True
66 try:
Jakub Josef2a7739b2017-01-24 18:33:44 +010067 _current_job_config = __salt__['jenkins.get_job_config'](name)
chnyda2d787312017-08-07 20:07:56 +020068 except salt.exceptions.SaltInvocationError as e:
69 if 'does not exists.' in str(e):
70 _job_exists = False
71 else:
72 raise e
73
chnyda90f133f2017-08-02 10:46:13 +020074
75 if _job_exists:
Jakub Josef2a7739b2017-01-24 18:33:44 +010076 buf = six.moves.StringIO(_current_job_config)
chnyda90f133f2017-08-02 10:46:13 +020077 oldXMLstring = buf.read()
Jakub Josefe3807982016-12-15 11:54:51 +010078
Jakub Josef2a7739b2017-01-24 18:33:44 +010079 cached_source_path = __salt__['cp.cache_file'](config, __env__)
80 with salt.utils.fopen(cached_source_path) as _fp:
chnyda90f133f2017-08-02 10:46:13 +020081 newXMLstring = _fp.read()
82 if not _elements_equal(oldXMLstring.strip(), newXMLstring.strip()):
83 oldXML = ET.fromstring(oldXMLstring)
84 newXML = ET.fromstring(newXMLstring)
Jakub Josef2a7739b2017-01-24 18:33:44 +010085 diff = difflib.unified_diff(
86 ET.tostringlist(oldXML, encoding='utf8', method='xml'),
87 ET.tostringlist(newXML, encoding='utf8', method='xml'), lineterm='')
88 __salt__['jenkins.update_job'](name, config, __env__)
Jakub Josef654a1482017-01-26 17:41:16 +010089 ret['changes'][name] = ''.join(diff)
chnyda90f133f2017-08-02 10:46:13 +020090 ret['comment'] = 'Job {0} updated.'.format(name)
Jakub Josefe3807982016-12-15 11:54:51 +010091
Jakub Josef2a7739b2017-01-24 18:33:44 +010092 else:
93 cached_source_path = __salt__['cp.cache_file'](config, __env__)
94 with salt.utils.fopen(cached_source_path) as _fp:
95 new_config_xml = _fp.read()
Jakub Josefe3807982016-12-15 11:54:51 +010096
Jakub Josef2a7739b2017-01-24 18:33:44 +010097 __salt__['jenkins.create_job'](name, config, __env__)
Jakub Josefe3807982016-12-15 11:54:51 +010098
Jakub Josef2a7739b2017-01-24 18:33:44 +010099 buf = six.moves.StringIO(new_config_xml)
Jakub Josef654a1482017-01-26 17:41:16 +0100100 diff = difflib.unified_diff('', buf.readlines(), lineterm='')
101 ret['changes'][name] = ''.join(diff)
Jakub Josef2a7739b2017-01-24 18:33:44 +0100102 ret['comment'].append('Job {0} added.'.format(name))
103
104 ret['comment'] = '\n'.join(ret['comment'])
Jakub Josefe3807982016-12-15 11:54:51 +0100105 return ret
106
107
108def absent(name,
109 **kwargs):
110 '''
111 Ensure the job is present in the Jenkins
112 configured jobs
113
114 name
115 The name of the Jenkins job to remove.
116
117 '''
Jakub Josef2a7739b2017-01-24 18:33:44 +0100118 test = __opts__['test']
Jakub Josefe3807982016-12-15 11:54:51 +0100119 ret = {'name': name,
120 'result': True,
121 'changes': {},
122 'comment': []}
Jakub Josef2a7739b2017-01-24 18:33:44 +0100123 if test:
124 status = 'DELETED'
125 ret['changes'][name] = status
126 ret['comment'] = 'Node %s %s' % (name, status.lower())
Jakub Josefe3807982016-12-15 11:54:51 +0100127 else:
Jakub Josef2a7739b2017-01-24 18:33:44 +0100128 _job_exists = __salt__['jenkins.job_exists'](name)
129
130 if _job_exists:
131 __salt__['jenkins.delete_job'](name)
132 ret['comment'] = 'Job {0} deleted.'.format(name)
133 else:
134 ret['comment'] = 'Job {0} already absent.'.format(name)
135 return ret
136
137
138def cleanup(name, jobs, **kwargs):
139 '''
140 Perform a cleanup - uninstall any installed job absents in given jobs list
141
142 name
143 The name of the Jenkins job to remove.
144 jobs
145 List of jobs which may NOT be uninstalled
146
147 '''
148 test = __opts__['test']
149 ret = {'name': name,
150 'result': True,
151 'changes': {},
152 'comment': "Cleanup not necessary"}
153 list_jobs_groovy = """\
154 print(Jenkins.instance.items.collect{{it -> it.name}})
155 """
156 deleted_jobs = []
157 if test:
158 status = 'CLEANED'
159 ret['changes'][name] = status
160 ret['comment'] = 'Jobs %s' % status.lower()
161 else:
162 call_result = __salt__['jenkins_common.call_groovy_script'](list_jobs_groovy,{})
163 if call_result["code"] == 200:
164 existing_jobs = call_result["msg"]
165 if existing_jobs:
166 for job in existing_jobs[1:-1].split(","):
167 if job:
168 job = job.strip()
169 if job not in jobs:
170 __salt__['jenkins.delete_job'](job)
171 deleted_jobs.append(job)
172 else:
173 log.error("Cannot get existing jobs list from Jenkins")
174 if len(deleted_jobs) > 0:
175 for del_job in deleted_jobs:
176 ret['changes'][del_job] = "removed"
177 ret['comment'] = 'Jobs {} deleted.'.format(deleted_jobs)
178 else:
179 status = 'FAILED'
180 log.error(
181 "Jenkins jobs API call failure: %s", call_result["msg"])
182 ret['comment'] = 'Jenkins jobs API call failure: %s' % (
183 call_result["msg"])
Jakub Josefe3807982016-12-15 11:54:51 +0100184 return ret