Python利用requests库自己写一个Jenkins的api库之节点的操作

416 阅读5分钟

在前面远程启动Jenkins工程的文章中,我们已经初步的接触到了API方式的调用,这种方式是通过网络请求进行的,我们前面使用的jenkinsapi库,以及其他的诸如Python-Jenkins、api4jenkins,aiojenkins等库都是实现的这个功能。对于上一篇文章中的断开节点链接,jenkinsapi库里是没有这个功能的,那么我们是不是也可以去寻找api实现他。

如果还是用的Python代码的话,那么事情就变成了使用requests库来实现功能了,更为通用的功能是将其写入js中,这样哪个语言都可能集成的了。

身份认证

身份认证可以使用requests库里的HTTPBasicAuth


from requests.auth import HTTPBasicAuth

 

auth = HTTPBasicAuth(username='bob', password='110c70ea8f41063e377aca53007a1ca2e4')

 

这个类似于创建Jenkins实例时输入的账号和token,并且是默认use_crumb为True的情况。

jks = Jenkins('http://127.0.0.1:8080/', username='bob', password='110c70ea8f41063e377aca53007a1ca2e4',  
              use_crumb=True)

后续在发起请求的时候,我们只要在请求方法调用参数里加上auth=auth即可。

断开节点连接的api和其他api的查找方法

注:Jenkins中节点和代理一直用的2套中英文,但是实际看来是一个东西。

查询类的api其实很容易找到,只需要看Jenkins页面,右下角有REST API的就是可以执行远程操作的功能。

image.png

这个页面里面列举了一些基本的api方式,但明显是不全的,我们要的功能并不存在。

因为是计划用requests来执行操作,所以我按照requests的思维,选择去浏览器抓取工作链接。

进入到断开链接的页面后打开控制台-网络

image.png

点击是按钮

image.png

可以看到第一个跳出来的是doDisconnect请求。

  image.png

复制这个请求。选择复制为cURL(bash) 的方式

进入curl converter页面进行转换即可。

节点断开连接

关键区域的代码如下:

data = {  
    'offlineMessage': '', 'Jenkins-Crumb': 'b73836120dc03620b4bd46d5e1147b6cdd08879096b594a61a9a50701d45fba5',  
    'json': '{"offlineMessage": "", "Jenkins-Crumb": "b73836120dc03620b4bd46d5e1147b6cdd08879096b594a61a9a50701d45fba5"}',  
    'Submit': '是'  
}  
response = requests.post('http://127.0.0.1:8080/computer/test4/doDisconnect', cookies=cookies, headers=headers, data=data, verify=False, )

拉下来自己调试,经过分析,data里除了offlineMessage,其他的都是不必要的。

将原有节点重新连接后,通过代码进行断开。

完整代码如下:

# coding:utf-8
import requests  
from requests.auth import HTTPBasicAuth  
  
auth = HTTPBasicAuth(username='bob', password='110c70ea8f41063e377aca53007a1ca2e4')  
  
data = {  
    'offlineMessage': '通过requests提交关闭',  
}  
response = requests.post('http://127.0.0.1:8080/computer/test4/doDisconnect', auth=auth,data=data)

 

执行后界面显示

image.png

断开后如果代理能够被连接,会被断开重试的机制重新连接上,这和在网页上点击断开的效果是一样的,所以并不是代码的异常。

 

启动代理链接

如果需要启动代理。我们可以点击这个启动代理的按钮,然后提取浏览器的请求。

 

image.png

image.png

请求的路由是

POST /computer/test4/launchSlaveAgent

data里没有实际的信息。可以不用。

response = requests.post('http://127.0.0.1:8080/computer/test4/launchSlaveAgent', auth=auth)

这个调试的时候速度要快,不然等会儿节点自己就连接上了

节点临时上下线

同样的,临时上下线的功能也是这样提取

临时上下线:

POST /computer/test4/toggleOffline

数据data为

'offlineMessage': 'message',

重新运行一遍就又临时的上线了。这个是共用的同一个路由。

闲置状态查询

是否闲置的话就是直接查询节点状态 

GET /computer/test4/api/json

这里因为api/python的结果不建议用eval直接转换,并且为了其他语言读者更加通用,这里使用的是api/json。读取结果中的idle键的值就可以了。

image.png 需要注意的是节点的idle为True的时候并不只是没任务,连接断开或者临时断开都会导致节点是闲置的。这种代表了我们不能光看机器闲置就觉得机器是可用的,必须要机器既在线又是闲置才是可以被任务使用的。

配置查询

在浏览器里显示的是

GET /computer/test4/configure

但很明显这种GET的只能拿到一个网页数据,从REST API页面的介绍中可以看到实际应该是这个

GET /computer/test4/config.xml

实际得到的是一个xml格式的字符串

image.png  

修改配置

在网页端保存数据截取记录,获得的路由是

POST /computer/test4/configSubmit

 

转换后的数据字段极为复杂。

image.png

除了能够批量替换的修改,不建议这样使用。

还是通过下面这个请求来处理

POST /computer/test4/config.xml

jenkinsapi的库也是这样处理的,但是对于新版本Jenkins而言,他缺少了2个关键的请求头参数。

分别是Content-Length和Host,导致库无法正常实现这个功能。

于是我们实现的代码是:

old_config = """<?xml version="1.1" encoding="UTF-8"?>  
<slave>  
  <name>test4</name>  
  <description></description>  
  <remoteFS>/data</remoteFS>  
  <numExecutors>5</numExecutors>  
  <mode>NORMAL</mode>  
  <retentionStrategy class="hudson.slaves.RetentionStrategy$Always"/>  
  <launcher class="hudson.plugins.sshslaves.SSHLauncher" plugin="ssh-slaves@2.854.v7fd446b_337c9">  
    <host>127.0.0.1</host>  
    <port>10009</port>  
    <credentialsId>0f4ad930-938b-4440-9d0d-14aae7951b61</credentialsId>  
    <jvmOptions>-Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 -Dnative.encoding=UTF-8</jvmOptions>  
    <launchTimeoutSeconds>60</launchTimeoutSeconds>  
    <maxNumRetries>10</maxNumRetries>  
    <retryWaitTime>15</retryWaitTime>  
    <sshHostKeyVerificationStrategy class="hudson.plugins.sshslaves.verifiers.NonVerifyingKeyVerificationStrategy"/>  
    <tcpNoDelay>true</tcpNoDelay>  
  </launcher>  
  <label>test</label>  
    
  <nodeProperties/>  
</slave>"""  
new_config = old_config.replace('<numExecutors>2</numExecutors>', '<numExecutors>3</numExecutors>')  
raw_data = new_config.encode('utf-8')  
content_length = len(raw_data)  
host = '127.0.0.1'  
headers = {  
    'Content-Length': str(content_length),  
    'Host':host  
}  
response = requests.post('http://127.0.0.01:8080/computer/test4/config.xml', auth=auth,  
                         data=raw_data, headers=headers)  
print(response.status_code)  
print(response.text)

注意这里的config字符串要符合xml规范且头尾无多余空格与换行。修改原有的配置可以通过xml节点进行操作,也可以通过替换字符串操作。最终是个bytes类型。

正常更新的响应体是空的,状态码是200。

可以看到节点的配置修改记录里面出现了我们的远程修改。经过调试可以发现,当修改没有差异的时候,不会发生记录。

image.png

删除节点

POST /computer/test4/doDelete

执行完就把节点删除了。重复执行只会得到404的响应状态码。

增加节点

POST /computer/doCreateItem

node_name = 'test_node_1'  
params = {  
    'name': node_name, 'type': 'hudson.slaves.DumbSlave$DescriptorImpl',  
    'json': '{"name": "%s", "nodeDescription": null, "numExecutors": 2, "remoteFS": "/var/lib/jenkins", "labelString": null, "mode": "NORMAL", "retentionStrategy": {"stapler-class": "hudson.slaves.RetentionStrategy$Always", "$class": "hudson.slaves.RetentionStrategy$Always"}, "type": "hudson.slaves.DumbSlave", "nodeProperties": {"stapler-class-bag": "true"}, "launcher": {"stapler-class": "hudson.slaves.JNLPLauncher"}}'%node_name  
}  
print(params)  
from urllib.parse import urlencode  
  
data = {  
    'json': urlencode(params)  
}  
response = requests.post(f'http://127.0.0.1:8080/computer/doCreateItem', auth=auth, data=data,params=params)

因为jnlp的默认节点用的不多,所以这里在配置项目只设置了名称,建议后续依靠其他节点的配置进行修改。 这个请求比较特殊,一定要把参数在data和params都放一遍。