blob: 64c5544540027d70f967bdf5774402c77b70d97b [file] [log] [blame]
Jakub Josef8e7385e2016-12-07 21:20:34 +01001import logging
Ilya Kharin3d8bffe2017-06-22 17:40:31 +04002
Jakub Josef8e7385e2016-12-07 21:20:34 +01003from salt.exceptions import SaltInvocationError
4
Ilya Kharin3d8bffe2017-06-22 17:40:31 +04005try:
6 import bcrypt
7 HAS_BCRYPT = True
8except ImportError:
9 HAS_BCRYPT = False
10
11try:
12 import requests
13 HAS_REQUESTS = True
14except ImportError:
15 HAS_REQUESTS = False
16
Jakub Josef8e7385e2016-12-07 21:20:34 +010017logger = logging.getLogger(__name__)
18
19
Ilya Kharin3d8bffe2017-06-22 17:40:31 +040020def __virtual__():
21 '''
22 Only load if bcrypt and requests libraries exist.
23 '''
24 if not HAS_BCRYPT:
25 return (
26 False,
27 'Can not load module jenkins_common: bcrypt library not found')
28 if not HAS_REQUESTS:
29 return (
30 False,
31 'Can not load module jenkins_common: requests library not found')
32 return True
33
34
Jakub Josef7ae6b242016-12-14 14:41:44 +010035def call_groovy_script(script, props, username=None, password=None, success_status_codes=[200]):
Jakub Josef8e7385e2016-12-07 21:20:34 +010036 """
37 Common method for call Jenkins groovy script API
38
Jakub Josef7ae6b242016-12-14 14:41:44 +010039 :param script: groovy script template
40 :param props: groovy script properties
41 :param username: jenkins username (optional,
42 if missing creds from sall will be used)
43 :param password: jenkins password (optional,
44 if missing creds from sall will be used)
45 :param success_status_codes: success response status code
46 (optional) in some cases we want to declare error call as success
Jakub Josef8e7385e2016-12-07 21:20:34 +010047 :returns: HTTP dict {status,code,msg}
48 """
49 ret = {
50 "status": "FAILED",
51 "code": 999,
52 "msg": ""
53 }
54 jenkins_url, jenkins_user, jenkins_password = get_jenkins_auth()
Jakub Josef7ae6b242016-12-14 14:41:44 +010055 if username:
56 jenkins_user = username
57 if password:
58 jenkins_password = password
59
Jakub Josef8e7385e2016-12-07 21:20:34 +010060 if not jenkins_url:
61 raise SaltInvocationError('No Jenkins URL found.')
Jakub Josefe13e2e72016-12-08 13:41:19 +010062
63 token_obj = get_api_crumb(jenkins_url, jenkins_user, jenkins_password)
64 req_data = {"script": render_groovy_script(script, props)}
65 if token_obj:
66 req_data[token_obj["crumbRequestField"]] = token_obj["crumb"]
67
68 logger.debug("Calling Jenkins script API with URL: %s", jenkins_url)
69 req = requests.post('%s/scriptText' % jenkins_url,
70 auth=(jenkins_user, jenkins_password),
71 data=req_data)
72 ret["code"] = req.status_code
Jakub Josef063a7532017-01-11 15:48:01 +010073 ret["msg"] = req.text
Jakub Josef7ae6b242016-12-14 14:41:44 +010074 if req.status_code in success_status_codes:
Jakub Josefe13e2e72016-12-08 13:41:19 +010075 ret["status"] = "SUCCESS"
Jakub Josefe13e2e72016-12-08 13:41:19 +010076 logger.debug("Jenkins script API call success: %s", ret)
Jakub Josef8e7385e2016-12-07 21:20:34 +010077 else:
Jakub Josefe13e2e72016-12-08 13:41:19 +010078 logger.error("Jenkins script API call failed. \
79 Return code %s. Text: %s", req.status_code, req.text)
Jakub Josef8e7385e2016-12-07 21:20:34 +010080 return ret
81
82
83def render_groovy_script(script, props):
84 """
85 Helper method for rendering groovy script with props
86
Jakub Josef3de91af2016-12-08 17:03:33 +010087 :param script: groovy script template
88 :param props: groovy script properties
Jakub Josef8e7385e2016-12-07 21:20:34 +010089 :returns: generated groovy script
90 """
91 return script.format(**props)
92
93
94def get_api_crumb(jenkins_url=None, jenkins_user=None, jenkins_password=None):
95 """
96 Obtains Jenkins API crumb, if CSRF protection is enabled.
97 Jenkins params can be given by params or not, if not,
98 params will be get from salt.
99
100 :param jenkins_url: Jenkins URL (optional)
101 :param jenkins_user: Jenkins admin username (optional)
102 :param jenkins_password: Jenkins admin password (optional)
103 :returns: salt-specified state dict
104 """
105 if not jenkins_url:
106 jenkins_url, jenkins_user, jenkins_password = get_jenkins_auth()
107 logger.debug("Obtaining Jenkins API crumb for URL: %s", jenkins_url)
108 tokenReq = requests.get("%s/crumbIssuer/api/json" % jenkins_url,
Jakub Josefe13e2e72016-12-08 13:41:19 +0100109 auth=(jenkins_user, jenkins_password) if jenkins_user else None)
Jakub Josef8e7385e2016-12-07 21:20:34 +0100110 if tokenReq.status_code == 200:
111 return tokenReq.json()
Jakub Josef7ae6b242016-12-14 14:41:44 +0100112 elif tokenReq.status_code in [404, 401]:
113 # 404 means CSRF security is disabled, so api crumb is not necessary,
114 # 401 means unauthorized
Jakub Josefe13e2e72016-12-08 13:41:19 +0100115 return None
Jakub Josef8e7385e2016-12-07 21:20:34 +0100116 else:
Jakub Josefe13e2e72016-12-08 13:41:19 +0100117 raise Exception("Cannot obtain Jenkins API crumb. Status code: %s. Text: %s" %
Jakub Josef3de91af2016-12-08 17:03:33 +0100118 (tokenReq.status_code, tokenReq.text))
Jakub Josef8e7385e2016-12-07 21:20:34 +0100119
120
Jakub Josef8e7385e2016-12-07 21:20:34 +0100121def get_jenkins_auth():
122 """
123 Get jenkins params from salt
124 """
125 jenkins_url = __salt__['config.get']('jenkins.url') or \
126 __salt__['config.get']('jenkins:url') or \
127 __salt__['pillar.get']('jenkins.url')
128
129 jenkins_user = __salt__['config.get']('jenkins.user') or \
130 __salt__['config.get']('jenkins:user') or \
131 __salt__['pillar.get']('jenkins.user')
132
133 jenkins_password = __salt__['config.get']('jenkins.password') or \
134 __salt__['config.get']('jenkins:password') or \
135 __salt__['pillar.get']('jenkins.password')
136
137 return (jenkins_url, jenkins_user, jenkins_password)
138
139
140def encode_password(password):
141 """
142 Hash plaintext password by jenkins bcrypt algorithm
143 :param password: plain-text password
144 :returns: bcrypt hashed password
145 """
146 if isinstance(password, str):
147 return bcrypt.hashpw(password, bcrypt.gensalt(prefix=b"2a"))