blob: 20ff13179250620dad562f39201b36d1f4b1300c [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)
chnyda90f133f2017-08-02 10:46:13 +020068 except jenkins.NotFoundException:
69 _job_exists = False
70
71 if _job_exists:
Jakub Josef2a7739b2017-01-24 18:33:44 +010072 buf = six.moves.StringIO(_current_job_config)
chnyda90f133f2017-08-02 10:46:13 +020073 oldXMLstring = buf.read()
Jakub Josefe3807982016-12-15 11:54:51 +010074
Jakub Josef2a7739b2017-01-24 18:33:44 +010075 cached_source_path = __salt__['cp.cache_file'](config, __env__)
76 with salt.utils.fopen(cached_source_path) as _fp:
chnyda90f133f2017-08-02 10:46:13 +020077 newXMLstring = _fp.read()
78 if not _elements_equal(oldXMLstring.strip(), newXMLstring.strip()):
79 oldXML = ET.fromstring(oldXMLstring)
80 newXML = ET.fromstring(newXMLstring)
Jakub Josef2a7739b2017-01-24 18:33:44 +010081 diff = difflib.unified_diff(
82 ET.tostringlist(oldXML, encoding='utf8', method='xml'),
83 ET.tostringlist(newXML, encoding='utf8', method='xml'), lineterm='')
84 __salt__['jenkins.update_job'](name, config, __env__)
Jakub Josef654a1482017-01-26 17:41:16 +010085 ret['changes'][name] = ''.join(diff)
chnyda90f133f2017-08-02 10:46:13 +020086 ret['comment'] = 'Job {0} updated.'.format(name)
Jakub Josefe3807982016-12-15 11:54:51 +010087
Jakub Josef2a7739b2017-01-24 18:33:44 +010088 else:
89 cached_source_path = __salt__['cp.cache_file'](config, __env__)
90 with salt.utils.fopen(cached_source_path) as _fp:
91 new_config_xml = _fp.read()
Jakub Josefe3807982016-12-15 11:54:51 +010092
Jakub Josef2a7739b2017-01-24 18:33:44 +010093 __salt__['jenkins.create_job'](name, config, __env__)
Jakub Josefe3807982016-12-15 11:54:51 +010094
Jakub Josef2a7739b2017-01-24 18:33:44 +010095 buf = six.moves.StringIO(new_config_xml)
Jakub Josef654a1482017-01-26 17:41:16 +010096 diff = difflib.unified_diff('', buf.readlines(), lineterm='')
97 ret['changes'][name] = ''.join(diff)
Jakub Josef2a7739b2017-01-24 18:33:44 +010098 ret['comment'].append('Job {0} added.'.format(name))
99
100 ret['comment'] = '\n'.join(ret['comment'])
Jakub Josefe3807982016-12-15 11:54:51 +0100101 return ret
102
103
104def absent(name,
105 **kwargs):
106 '''
107 Ensure the job is present in the Jenkins
108 configured jobs
109
110 name
111 The name of the Jenkins job to remove.
112
113 '''
Jakub Josef2a7739b2017-01-24 18:33:44 +0100114 test = __opts__['test']
Jakub Josefe3807982016-12-15 11:54:51 +0100115 ret = {'name': name,
116 'result': True,
117 'changes': {},
118 'comment': []}
Jakub Josef2a7739b2017-01-24 18:33:44 +0100119 if test:
120 status = 'DELETED'
121 ret['changes'][name] = status
122 ret['comment'] = 'Node %s %s' % (name, status.lower())
Jakub Josefe3807982016-12-15 11:54:51 +0100123 else:
Jakub Josef2a7739b2017-01-24 18:33:44 +0100124 _job_exists = __salt__['jenkins.job_exists'](name)
125
126 if _job_exists:
127 __salt__['jenkins.delete_job'](name)
128 ret['comment'] = 'Job {0} deleted.'.format(name)
129 else:
130 ret['comment'] = 'Job {0} already absent.'.format(name)
131 return ret
132
133
134def cleanup(name, jobs, **kwargs):
135 '''
136 Perform a cleanup - uninstall any installed job absents in given jobs list
137
138 name
139 The name of the Jenkins job to remove.
140 jobs
141 List of jobs which may NOT be uninstalled
142
143 '''
144 test = __opts__['test']
145 ret = {'name': name,
146 'result': True,
147 'changes': {},
148 'comment': "Cleanup not necessary"}
149 list_jobs_groovy = """\
150 print(Jenkins.instance.items.collect{{it -> it.name}})
151 """
152 deleted_jobs = []
153 if test:
154 status = 'CLEANED'
155 ret['changes'][name] = status
156 ret['comment'] = 'Jobs %s' % status.lower()
157 else:
158 call_result = __salt__['jenkins_common.call_groovy_script'](list_jobs_groovy,{})
159 if call_result["code"] == 200:
160 existing_jobs = call_result["msg"]
161 if existing_jobs:
162 for job in existing_jobs[1:-1].split(","):
163 if job:
164 job = job.strip()
165 if job not in jobs:
166 __salt__['jenkins.delete_job'](job)
167 deleted_jobs.append(job)
168 else:
169 log.error("Cannot get existing jobs list from Jenkins")
170 if len(deleted_jobs) > 0:
171 for del_job in deleted_jobs:
172 ret['changes'][del_job] = "removed"
173 ret['comment'] = 'Jobs {} deleted.'.format(deleted_jobs)
174 else:
175 status = 'FAILED'
176 log.error(
177 "Jenkins jobs API call failure: %s", call_result["msg"])
178 ret['comment'] = 'Jenkins jobs API call failure: %s' % (
179 call_result["msg"])
Jakub Josefe3807982016-12-15 11:54:51 +0100180 return ret