blob: 69deb37560095c533f5bc96483032ca7462b2117 [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
18def _elements_equal(e1, e2):
19 if e1.tag != e2.tag:
20 return False
21 if e1.text != e2.text:
22 return False
23 if e1.tail != e2.tail:
24 return False
25 if e1.attrib != e2.attrib:
26 return False
27 if len(e1) != len(e2):
28 return False
29 return all(_elements_equal(c1, c2) for c1, c2 in zip(e1, e2))
30
31
32def present(name,
33 config=None,
34 **kwargs):
35 '''
36 Ensure the job is present in the Jenkins
37 configured jobs
38 name
39 The unique name for the Jenkins job
40 config
41 The Salt URL for the file to use for
42 configuring the job.
43 '''
Jakub Josef2a7739b2017-01-24 18:33:44 +010044 test = __opts__['test']
Jakub Josefe3807982016-12-15 11:54:51 +010045 ret = {'name': name,
46 'result': True,
47 'changes': {},
48 'comment': ['Job {0} is up to date.'.format(name)]}
Jakub Josef2a7739b2017-01-24 18:33:44 +010049 if test:
50 status = 'CREATED'
51 ret['changes'][name] = status
52 ret['comment'] = 'Job %s %s' % (name, status.lower())
Jakub Josefe3807982016-12-15 11:54:51 +010053 else:
Jakub Josef2a7739b2017-01-24 18:33:44 +010054 _job_exists = __salt__['jenkins.job_exists'](name)
55 if _job_exists:
56 _current_job_config = __salt__['jenkins.get_job_config'](name)
57 buf = six.moves.StringIO(_current_job_config)
58 oldXML = ET.fromstring(buf.read())
Jakub Josefe3807982016-12-15 11:54:51 +010059
Jakub Josef2a7739b2017-01-24 18:33:44 +010060 cached_source_path = __salt__['cp.cache_file'](config, __env__)
61 with salt.utils.fopen(cached_source_path) as _fp:
62 newXML = ET.fromstring(_fp.read())
63 if not _elements_equal(oldXML, newXML):
64 diff = difflib.unified_diff(
65 ET.tostringlist(oldXML, encoding='utf8', method='xml'),
66 ET.tostringlist(newXML, encoding='utf8', method='xml'), lineterm='')
67 __salt__['jenkins.update_job'](name, config, __env__)
Jakub Josef654a1482017-01-26 17:41:16 +010068 ret['changes'][name] = ''.join(diff)
Jakub Josef2a7739b2017-01-24 18:33:44 +010069 ret['comment'].append('Job {0} updated.'.format(name))
Jakub Josefe3807982016-12-15 11:54:51 +010070
Jakub Josef2a7739b2017-01-24 18:33:44 +010071 else:
72 cached_source_path = __salt__['cp.cache_file'](config, __env__)
73 with salt.utils.fopen(cached_source_path) as _fp:
74 new_config_xml = _fp.read()
Jakub Josefe3807982016-12-15 11:54:51 +010075
Jakub Josef2a7739b2017-01-24 18:33:44 +010076 __salt__['jenkins.create_job'](name, config, __env__)
Jakub Josefe3807982016-12-15 11:54:51 +010077
Jakub Josef2a7739b2017-01-24 18:33:44 +010078 buf = six.moves.StringIO(new_config_xml)
Jakub Josef654a1482017-01-26 17:41:16 +010079 diff = difflib.unified_diff('', buf.readlines(), lineterm='')
80 ret['changes'][name] = ''.join(diff)
Jakub Josef2a7739b2017-01-24 18:33:44 +010081 ret['comment'].append('Job {0} added.'.format(name))
82
83 ret['comment'] = '\n'.join(ret['comment'])
Jakub Josefe3807982016-12-15 11:54:51 +010084 return ret
85
86
87def absent(name,
88 **kwargs):
89 '''
90 Ensure the job is present in the Jenkins
91 configured jobs
92
93 name
94 The name of the Jenkins job to remove.
95
96 '''
Jakub Josef2a7739b2017-01-24 18:33:44 +010097 test = __opts__['test']
Jakub Josefe3807982016-12-15 11:54:51 +010098 ret = {'name': name,
99 'result': True,
100 'changes': {},
101 'comment': []}
Jakub Josef2a7739b2017-01-24 18:33:44 +0100102 if test:
103 status = 'DELETED'
104 ret['changes'][name] = status
105 ret['comment'] = 'Node %s %s' % (name, status.lower())
Jakub Josefe3807982016-12-15 11:54:51 +0100106 else:
Jakub Josef2a7739b2017-01-24 18:33:44 +0100107 _job_exists = __salt__['jenkins.job_exists'](name)
108
109 if _job_exists:
110 __salt__['jenkins.delete_job'](name)
111 ret['comment'] = 'Job {0} deleted.'.format(name)
112 else:
113 ret['comment'] = 'Job {0} already absent.'.format(name)
114 return ret
115
116
117def cleanup(name, jobs, **kwargs):
118 '''
119 Perform a cleanup - uninstall any installed job absents in given jobs list
120
121 name
122 The name of the Jenkins job to remove.
123 jobs
124 List of jobs which may NOT be uninstalled
125
126 '''
127 test = __opts__['test']
128 ret = {'name': name,
129 'result': True,
130 'changes': {},
131 'comment': "Cleanup not necessary"}
132 list_jobs_groovy = """\
133 print(Jenkins.instance.items.collect{{it -> it.name}})
134 """
135 deleted_jobs = []
136 if test:
137 status = 'CLEANED'
138 ret['changes'][name] = status
139 ret['comment'] = 'Jobs %s' % status.lower()
140 else:
141 call_result = __salt__['jenkins_common.call_groovy_script'](list_jobs_groovy,{})
142 if call_result["code"] == 200:
143 existing_jobs = call_result["msg"]
144 if existing_jobs:
145 for job in existing_jobs[1:-1].split(","):
146 if job:
147 job = job.strip()
148 if job not in jobs:
149 __salt__['jenkins.delete_job'](job)
150 deleted_jobs.append(job)
151 else:
152 log.error("Cannot get existing jobs list from Jenkins")
153 if len(deleted_jobs) > 0:
154 for del_job in deleted_jobs:
155 ret['changes'][del_job] = "removed"
156 ret['comment'] = 'Jobs {} deleted.'.format(deleted_jobs)
157 else:
158 status = 'FAILED'
159 log.error(
160 "Jenkins jobs API call failure: %s", call_result["msg"])
161 ret['comment'] = 'Jenkins jobs API call failure: %s' % (
162 call_result["msg"])
Jakub Josefe3807982016-12-15 11:54:51 +0100163 return ret