使用Python调用Jenkinsapi接口之Node管理

节点简介

在 Jenkins 中,节点是指属于 Jenkins 环境并用于执行构建任务的计算机。节点可以分为两种类型:内置节点和 agent 节点。

  • 内置节点(Built-in Node)
    内置节点是 Jenkins 控制器本身的一部分。可以在此节点上运行任务,但由于安全性、性能和可伸缩性问题,通常不建议这样做。为内置节点配置的执行程序数量决定了其运行任务的能力。将 executor 数量设置为 0 将禁用内置节点上正在运行的任务。
  • 代理节点(Agent Nodes)
    代理节点是连接到 Jenkins 控制器并执行构建任务的独立计算机。这些节点可以在任何支持 Java 的作系统上运行。

代理节点说明

Jenkins基础服务中支持的代理节点从运行状态上来说可以分为固定节点&&动态生成节点。默认情况下Jenkins服务内置的代理节点只有固定节点,动态节点基本上是通过安装其他三方插件实现的,常见的动态节点插件有(K8S&&DOCKER等)
所以默认情况下JenkinsApi中只支持基于固定节点的管理操作。

  • 固定节点
    在Jenkins创建后,jenkins节点管理页面显示会一直记录此节点的一些信息。
  • 动态节点
    平时不会在Jenkins节点管理页面显示,只有具体流水线使用时,才可以在节点管理页面找到,并且该流水线执行结束后,相关节点信息就会消失(JenkinsApi接口不支持对其管理)。

固定节点启动的类型

针对Jenkins默认支持的固定节点,在启动的时候有以下几种方式实现:

  • 通过SSH启动代理

<slave>
  <name>sshsshname>
  <description>description>
  <remoteFS>/dataremoteFS>
  <numExecutors>1numExecutors>
  <mode>NORMALmode>
  <retentionStrategy class="hudson.slaves.RetentionStrategy$Always"/>
  <launcher class="hudson.plugins.sshslaves.SSHLauncher" plugin="[email protected]_c390e">
    <host>1.1.1.1host>
    <port>22port>
    <credentialsId>312313credentialsId>
    <launchTimeoutSeconds>60launchTimeoutSeconds>
    <maxNumRetries>10maxNumRetries>
    <retryWaitTime>15retryWaitTime>
    <sshHostKeyVerificationStrategy class="hudson.plugins.sshslaves.verifiers.KnownHostsFileKeyVerificationStrategy"/>
    <tcpNoDelay>truetcpNoDelay>
  launcher>
  <label>aaaalabel>
  <nodeProperties/>
slave>
  • 通过Java Web 启动代理(因为不需要知道agent节点的用户和密码登权限问题,所以此方式比较常用)

<slave>
  <name>2222name>
  <description>stringdescription>
  <remoteFS>/var/remoteFS>
  <numExecutors>0numExecutors>
  <mode>NORMALmode>
  <retentionStrategy class="hudson.slaves.RetentionStrategy$Always"/>
  <launcher class="hudson.slaves.JNLPLauncher">
    <workDirSettings>
      <disabled>falsedisabled>
      <internalDir>remotinginternalDir>
      <failIfWorkDirIsMissing>falsefailIfWorkDirIsMissing>
    workDirSettings>
    <webSocket>truewebSocket>
  launcher>
  <label>Linuxlabel>
  <nodeProperties/>
slave>
  • 通过在控制器上执行命令启动代理

<slave>
  <name>{{nodeName}}name>
  <description>{{nodeDescription}}description>
  <remoteFS>{{workDir}}remoteFS>
  <numExecutors>{{numbers}}numExecutors>
  <mode>NORMALmode>
  <retentionStrategy class="hudson.slaves.RetentionStrategy$Always"/>
  <launcher class="hudson.slaves.CommandLauncher" plugin="[email protected]_7c31">
    <agentCommand>{{Command}}agentCommand>
  launcher>
  <label>{{Labels}}label>
  <nodeProperties/>
slave>

固态节点的接口调用

  • 创建节点(通过Java Web 启动代理为例)
'''Create a node        
   :param name: name of node to create, ``str``   节点名字        
   :param numExecutors: number of executors for node, ``int``  节点最多能同时运行多少个流水线        
   :param nodeDescription: Description of node, ``str``    节点说明        
   :param remoteFS: Remote filesystem location to use, ``str``    远程节点干活的目录        
   :param labels: Labels to associate with node, ``str``      #节点标签        
   :param exclusive: Use this node for tied jobs only, ``bool``   是否尽可能使用该节点          
   :param launcher: The launch method for the slave, ``jenkins.LAUNCHER_COMMAND``, \        ``jenkins.LAUNCHER_SSH``, ``jenkins.LAUNCHER_JNLP``, ``jenkins.LAUNCHER_WINDOWS_SERVICE``  节点支持的启动类型        
   :param launcher_params: Additional parameters for the launcher, ``dict``  节点启动类型的额外参数        '''
import jenkins
j = jenkins.Jenkins(url="http://jenkins.demonlg.cn",username="admin",password="xxxx")
j.create_node(name="sssss",numExecutors=10,nodeDescription="这是一个测试节点",
              remoteFS="/home/node_agent",labels="aaa",exclusive=False,
              launcher=jenkins.LAUNCHER_JNLP,launcher_params={})
  • 修改节点
    修改节点的方法需要传入节点名字以及节点的xml配置,所以我们需要先使用接口获取出来节点的config配置信息然后再手动修改config信息后再次提交到Jenkins处。
    修改节点配置这里有2种方法:
    1、传统意义上的查出节点的xml内容,然后通过xml模块转成对象进行手工替换;(这种方式本文不介绍了)
import jenkins
import xml.etree.ElementTree as ET
j = jenkins.Jenkins(url="http://jenkins.demonlg.cn",username="admin",password="xxxx")
node_config = j.get_node_config(name="sssss") #这个方法返回的是 节点xml文件的字符串
root = ET.fromstring(node_config)
for i in root.iter('description'):
    new_rank = "我被修改了"
    i.text = str(我被修改了)
new_config_xml = ET.tostring(new_config).decode('utf-8')
j.reconfig_node("sssss",new_config_xml)

2、如果节点的属性参数均已可以固化,则可以利用模版+动态传参的形式实现相关的替换

import jenkins
from jinja2 import Template
new_xml_tempalte = """


  {{nodeName}}
  {{nodeDescription}}
  {{workDir}}
  {{numbers}}
  NORMAL
  
  
    {{Command}}
  
  
  
"""
j = jenkins.Jenkins(url="http://jenkins.demonlg.cn",username="admin",password="xxxx")
template = Template(new_xml_tempalte).render({"nodeName":"sssss","nodeDescription":"我是要修改的节点","workDir":"/var",
                 "numbers":10,"Command":"ls -al","Laebles":"text"})
j.reconfig_node("sssss",template)
  • 删除节点
import jenkins
j = jenkins.Jenkins(url="http://jenkins.demonlg.cn",username="admin",password="xxxx")
j.delete_node("sssss")
  • 显示所有节点
import jenkins
j = jenkins.Jenkins(url="http://jenkins.demonlg.cn",username="admin",password="xxxx")
node_list = j.get_nodes(depth=0) ##depth越大代表获取的信息越多
print(node_list)
  • 查看节点配置文件信息
import jenkins
j = jenkins.Jenkins(url="http://jenkins.demonlg.cn",username="admin",password="xxxx")
config = j.get_node_config("sssss")
print(config)
  • 查看节点信息
import jenkins
j = jenkins.Jenkins(url="http://jenkins.demonlg.cn",username="admin",password="xxxx")
info = j.get_node_info(name="sssss",depth=0)
print(info)
  • 启用节点
import jenkins
j = jenkins.Jenkins(url="http://jenkins.demonlg.cn",username="admin",password="xxxx")
j.enable(name="sssss")
  • 停用节点
import jenkins
j = jenkins.Jenkins(url="http://jenkins.demonlg.cn",username="admin",password="xxxx")
j.disable(name="sssss")
  • 断言节点是否存在
import jenkins
j = jenkins.Jenkins(url="http://jenkins.demonlg.cn",username="admin",password="xxxx")
j.assert_node_exists(name="sssss",exception_message="不存在") ###exception_message 自定义的消息体

动态节点接口调用扩展

本文介绍的动态节点主要涉及到2个方向(Docker Cloud &&Kubernetes Cloud)这2类动态节点都是基于第三方的jenkins插件,需要提前在jenkins服务器上安装对应插件。
安装完相应插件后在jenkins管理后台的节点管理中会多出一个云节点功能。此时就可以通过云节点功能去添加相关的配置然后实现部署动态的节点。

  • Docker Cloud 节点
    利用Docker的tcp远程管理功能实现在jenkins接入相关Docker的tcp管理操作以实现可以动态创建删除Docker容器
  • Kubernetes Cloud 节点
    利用K8S的api实现动态创建删除pod

遇到这种问题时,有的时候需要转化一下思路。Jenkins本身服务支持脚本命令行对服务进行一系列操作,恰巧JenkinsApi的run_script()方法也支持这种功能的调用,想到这里初步确定了一种跨语言调用等方式实现功能开发。
经过查看DockerPlugins源码&&Kubernetes Cloud源码找到了最终的解决办法。
解决办法:
编写groovy方法实现添加DockerCloud及Kubernetes Cloud等功能

  • Docker Cloud 相关代码(Demo)
# GROOVY操作使用的依赖
JENKINS_CLASS_IMPORT_STR = """
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
import groovy.json.JsonBuilder
import com.nirima.jenkins.plugins.docker.DockerCloud
import com.nirima.jenkins.plugins.docker.DockerDisabled
import com.nirima.jenkins.plugins.docker.DockerImagePullStrategy
import com.nirima.jenkins.plugins.docker.DockerTemplate
import com.nirima.jenkins.plugins.docker.DockerTemplateBase
import com.nirima.jenkins.plugins.docker.launcher.AttachedDockerComputerLauncher
import hudson.slaves.Cloud
import io.jenkins.docker.client.DockerAPI
import io.jenkins.docker.connector.DockerComputerAttachConnector
import io.jenkins.docker.connector.DockerComputerConnector
import io.jenkins.docker.connector.DockerComputerJNLPConnector
import jenkins.model.Jenkins
import org.jenkinsci.plugins.docker.commons.credentials.DockerServerEndpoint
\n
"""

# 列出所有的dockerCloud的名字
JENKINS_DOCKER_LIST = JENKINS_CLASS_IMPORT_STR + """
Jenkins jenkins = Jenkins.get()
clouds_list = []
for (i in jenkins.clouds){
 if (i){
 clouds_list.add("\'"+i.name+"\'")
 }
}
println clouds_list
"""

# 查询dockerCloud是否存在
DOCKER_CLOUD_EXIST = JENKINS_CLASS_IMPORT_STR + '''
Jenkins jenkins = Jenkins.get()
Cloud oldCloudOrNull = jenkins.clouds.getByName('%s')
if (oldCloudOrNull){
               println 'True'
               }
            else{
                println 'False'
            }
'''
# 新增容器云节点
ADD_DOCKER_CLOUD = JENKINS_CLASS_IMPORT_STR + """      
            Jenkins jenkins = Jenkins.get()     
              def cloudParameters = [
              serverUrl:                   'tcp://%s',
              name:                        '%s',
              containerCap:                0, // 0 means no cap
              credentialsId:            '',
              connectTimeout:              60,
              readTimeout:                 60,
              version:                  '',
              dockerHostname:           '',
              exposeDockerHost:         false,
              disabled:                 false,
              errorDuration:            (Integer)null,
            ]
                DockerComputerConnector computerConnector = new DockerComputerAttachConnector()
              Set cloudParametersHandledSpecially = [ 'serverUrl', 'credentialsId' ,'serverUrl' ,'credentialsId' ,'connectTimeout' ,'readTimeout' ,'version' ,'connectTimeout' ,'dockerHostname' ,'name' ]
            DockerAPI api = new DockerAPI(new DockerServerEndpoint(cloudParameters.serverUrl, cloudParameters.credentialsId))
            api.with {
              connectTimeout = cloudParameters.connectTimeout
              readTimeout = cloudParameters.readTimeout
              apiVersion = cloudParameters.version
              hostname = cloudParameters.dockerHostname
            }
            DockerCloud newCloud = new DockerCloud(
              cloudParameters.name,
              api,
              []
            )    
            def  a= jenkins.clouds.add(newCloud)
            println a
            println 'True'   
            """

# 删除容器云主机
REMOVE_DOCKER_CLOUD = JENKINS_CLASS_IMPORT_STR + """
Jenkins jenkins = Jenkins.get()
def res = jenkins.clouds.remove(jenkins.clouds.getByName(\'%s\'))
if (res){
    println 'True'
    }else{
    println 'False'
    } 
"""

# 列出所有的容器云的名字
JENKINS_DOCKER_CLOUD_LIST = JENKINS_CLASS_IMPORT_STR + """
Jenkins jenkins = Jenkins.get()
clouds_list = []
for (i in jenkins.clouds){
 if (i){
 clouds_list.add("\'"+i.name+"\'")
 }
}
println clouds_list
"""

# 列出Docker Cloud下所有模版的信息
DOCKER_TEMPLATE_LIST = JENKINS_CLASS_IMPORT_STR + """
Jenkins jenkins = Jenkins.get()
templates_list = []
def a = jenkins.clouds.getByName(\'%s\')
for (i in a.templates){
     //println i.labelString
     templates_list.add("\'"+i.labelString+"\'")
} 
println templates_list
"""
import jenkins
class DockerCloud(object):
    """jenkins中DockerCloud节点管理模块"""

    def __init__(self, conn):
        self.conn = conn

    def add(self, name, url):
        """
        向jenkins中添加dockerCloud节点,前提要素是jenkins中已经安装docker commons,docker plugins等相关插件
        :param name:  dockerCloud节点的名字,exp: node1
        :param url:   dockerCloud节点的链接方式,exp:1.1.1.1:2042
        :return:
        """
        if not self.exist(name):
            docker_could_instance = ADD_DOCKER_CLOUD % (name, url)
            c = self.conn.run_script(docker_could_instance)
            print(c)
            if not c:
                print('节点添加失败!')
            else:
                print("节点添加成功!")
        elif self.exist(name):
            print("节点已经存在,无法再次添加!")

    def remove(self, name):
        """
        删除dockerCloud节点
        :param name:  dockerCloud节点的名字,exp: node1
        :param url:   dockerCloud节点的链接方式,exp:1.1.1.1:2042
        :return:
        """
        if self.conn.run_script(REMOVE_DOCKER_CLOUD % name):
            print('节点删除成功!')
        else:
            print('节点删除失败!')

    def exist(self, name):
        """
        检查 dockerCloud是否已经存在
        :param name: dockerCloud节点的名字,exp: node1
        :return: 存在 True,不存在False,其他异常报错,None
        """
        res = self.conn.run_script(DOCKER_CLOUD_EXIST % name)
        if ast.literal_eval(res):
            return True
        else:
            return False

    def list(self, ):
        """
        查询docker节点相关详细配置内容
        :return:
        """
        res = ast.literal_eval(self.conn.run_script(JENKINS_DOCKER_CLOUD_LIST))
        if len(res) == 0:
            raise jenkins.EmptyResponseException('没找到可用的容器云节点!')

        return res



class DockerTemplate(object):
    """
    DockerCLoud中的docker模板管理功能
    """

    def __init__(self, conn, node_name):
        self.conn = conn
        self.name = node_name
        res = self.conn.run_script(DOCKER_CLOUD_EXIST % self.name)
        if not ast.literal_eval(res):
            raise NotDockerCloudException('jenkins上没有对应的容器云节点%s' % self.name)

    def exist(self, label_name):
        """
        :param label_name: 模板的标签名,此参数是要求唯一根目录已存在!的
        :return:
        """
        if label_name in self.list():
            print('模板已经在dockerCloud中')
            return True
        else:
            print('模板不在dockerCloud中')
            return False



    def list(self, ):
        res = ast.literal_eval(self.conn.run_script(DOCKER_TEMPLATE_LIST % self.name))
        if len(res) == 0:
            raise EmptyResponseException('没有对应的模板信息!')
        return res



j = jenkins.Jenkins(url="http://jenkins.demonlg.cn",username="admin",passowrd="xxxxx00")
docker_cloud = DockerCloud(j)
#添加Docker Cloud
docker_cloud.add("测试容器节点","http://1.1.1.1:2375")
#显示所有的Docker Cloud
docker_cloud.list() 
#删除指定DockerCloud
docker_cloud.remove("测试容器节点")

#向特定Docker Cloud中添加模版
docker_template = DockerTemplate(j,"测试容器节点")
#显示特定Docker Cloud下所有的模版
docker_template.list()
  • Kubernetes Cloud 相关代码(Demo)
####新增k8s云节点相关功能
import jenkins

class K8sNode(BaseModel):
    """
    k8s云节点数据结构
    """
    uid: str = Field(default=str(uuid.uuid1()).replace('-', ''),alias="Uid")
    name: str = Field(alias="Name")
    kubernetes_url: str = Field(alias="KubernetesUrl",default="\"\"")
    kubernetes_cert: str = Field(alias="KubernetesCert",default="\"\"")
    is_https: bool = Field(alias="IsHttps",default=False)
    use_proxy: bool = Field(alias="UseProxy",default=False)
    namespace: str = Field(alias="Namespace",default="")
    jnlp_docker_registry: str = Field(alias="JnlpDockerRegistry",default="\"\"")
    kubernetes_secret: str = Field(alias="KubernetesSecret",default="对应k8s相关的授权凭据id")
    is_websocket: bool = Field(alias="IsWebsocket",default=False)
    is_direct: bool = Field(alias="IsDirect",default=False)
    jenkins_url: str = Field(alias="JenkinsUrl",default="\"\"")
    connection_timeout: int = Field(alias="ConnectionTimeout",default=5)
    read_timeout: int = Field(alias="ReadTimeout",default=15)
    max_containter_num: int = Field(alias="MaxContainerNum",default=100)
    retention_timout: int = Field(alias="RetentionTimout",default=120)

class K8SCould(jenkins.Jenkins):
    """
    显示K8SCould插件类型节点信息, 全查
    """
    def get_node(self,uid):
        """
        单独查询
        """
        display_command = """import groovy.json.JsonOutput
def res = [:]
def data = [:]
try{
    def k8s = Jenkins.get().clouds.getByName(\'%s\')
        data.Uid = \"%s\"
        data.KubernetesUrl = k8s.getServerUrl()
        data.KubernetesCert = k8s.getServerCertificate()
        data.IsHttps = k8s.isSkipTlsVerify()
        data.UseProxy = k8s.isUseJenkinsProxy()
        data.Namespace = k8s.getNamespace()
        data.JnlpDockerRegistry = k8s.getJnlpregistry()
        data.KubernetesSecret = k8s.getCredentialsId()
        data.IsWebsocket = k8s.isWebSocket()
        data.IsDirect = k8s.isDirectConnection()
        data.JenkinsUrl = k8s.getJenkinsUrlOrNull()
        data.ConnectionTimeout = k8s.getConnectTimeout()
        data.ReadTimeout = k8s.getReadTimeout()
        data.MaxContainerNum = k8s.getContainerCap()
        data.RetentionTimout = k8s.getRetentionTimeout()
        res.code = 1000
        res.msg = "操作成功"
        res.data = data
        
}catch(e){
    res.code = 1002
    res.msg = "操作异常:" +e
}
println( JsonOutput.toJson(res))"""%(uid,uid)
        try:
            match self.is_exist(uid).get("code"):
                case 1000:
                    return orjson.loads(self.run_script(display_command))
                case _:
                    return self.is_exist(uid)
        except orjson.JSONDecodeError:
                return {'code': 1002, 'msg': '脚本返回数据异常无法解析'}
        except Exception:
            return {'code': 1002, 'msg': '未知错误:'+traceback.format_exc()}

    def list(self,):
       """
        显示jenkins中存在的k8s云节点控制器
        """
       nodes = """import groovy.json.JsonOutput
def res = [:]
def a = []
try{
jenkins = Jenkins.get().clouds

for ( i in jenkins){
  if (i instanceof org.csanchez.jenkins.plugins.kubernetes.KubernetesCloud ){
        def aa = [:]
    	aa.name = i.name
        aa.url = i.serverUrl
        a.add(aa)
  }
 res.code = 1000
 res.mgs = "操作成功"
 res.data = a
}}catch(e){
res.code = 1002
 res.msg = "操作失败"
 res.data = a
}
println( JsonOutput.toJson(res)) """

       try:
           return orjson.loads( self.run_script(nodes))
       except orjson.JSONDecodeError:
           return {'code': 1002, 'msg': '脚本返回数据异常无法解析'}
       except Exception:
           return {'code': 1002, 'msg': '未知错误'+traceback.format_exc()}

    def add_node(self,k8s_info :K8sNode ):
        """
        用于添加Jenkins云节点。 新增
        k8s_info: 添加k8s云节点相关的参数

        """
        add_command = """import org.csanchez.jenkins.plugins.kubernetes.KubernetesCloud
        import groovy.json.JsonOutput
        def res = [:]
        try{
        def a = new KubernetesCloud(name=\'%s\',templates=[],serverUrl=%s,namespace=%s,jenkinsUrl=%s,containerCapStr='%s',connectTimeout=%s,readTimeout=%s,retentionTimout=%s)
        a.setSkipTlsVerify(%s)
        a.setWebSocket(%s)
        a.setUsageRestricted(%s)
        a.setCredentialsId(\'%s\')
        Jenkins.get().clouds.add(a)
        res.msg ='操作成功'
        res.code = 1000
        println(JsonOutput.toJson(res))
        }catch(e){
        res.msg ='操作失败:'+e
        res.code = 1001
        println(JsonOutput.toJson(res))
        }"""%(k8s_info.uid,k8s_info.kubernetes_url,k8s_info.namespace,
             k8s_info.jenkins_url,k8s_info.max_containter_num,k8s_info.connection_timeout,
             k8s_info.read_timeout,k8s_info.retention_timout,"true" if k8s_info.is_https else "false","true" if k8s_info.is_websocket else "false",
             "true" if k8s_info.is_direct else "false",k8s_info.kubernetes_secret)

        try:
            match  self.is_exist(k8s_info.uid).get('code'):
                case 1001:
                    return orjson.loads(self.run_script(add_command))
                case 1000:
                    return {'code': 1001, 'msg': '节点存在'}
                case _:
                    return self.is_exist(k8s_info.uid)
        except orjson.JSONDecodeError:
            return {'code': 1002, 'msg': '脚本返回数据异常无法解析'}
        except Exception:
            print(traceback.print_exc())
            return {'code': 1002, 'msg': '未知错误'}

    def remove_node(self,uid):
        """
        删除节点信息
        """
        delete_command = """
        import groovy.json.JsonOutput
        def res = [:]
        try{
        Jenkins.get().clouds.remove(Jenkins.get().clouds.getByName(\'%s\'))
        res.code = 1000
        res.msg = "操作成功"}catch(e){
        res.code = 1001
        res.msg = "操作失败:"+e
        }
        println(JsonOutput.toJson(res))
        """%uid
        try:
            match self.is_exist(uid).get('code'):
                case 1000:
                   return  orjson.loads(self.run_script(delete_command))
                case _:
                    return self.is_exist(uid)
        except orjson.JSONDecodeError:
            return {'code': 1002, 'msg': '脚本返回数据异常无法解析'}
        except Exception:
            return {'code': 1002, 'msg': '未知错误'}

    def is_exist(self, uid: str):
        """
         用于查找k8s节点是否存在。
        :param uid:  uid k8s阶段的唯一标识
        """

        is_exist_command= '''import groovy.json.JsonOutput
def res = [:]
Jenkins jenkins = Jenkins.get()
try{
    def oldCloudOrNull = jenkins.clouds.getByName('%s')
    if (oldCloudOrNull) {
        if (oldCloudOrNull instanceof org.csanchez.jenkins.plugins.kubernetes.KubernetesCloud) {
            res.code = 1000
            res.msg = "节点存在"
        } else {
            res.code = 1001
            res.msg = "不属于云节点类型:"+oldCloudOrNull.class.name
        }
    }
        else{
            res.code = 1001
            res.msg = "节点不存在"
        }}catch(e){
            res.code = 1002
            res.msg = "操作失败:" +e
        }
        println JsonOutput.toJson(res)
        '''%uid
        try:
            return  orjson.loads(self.run_script(is_exist_command))
        except orjson.JSONDecodeError:
           return {'code': 1002, 'msg': '脚本返回数据异常无法解析'}
        except Exception:
            return {'code': 1002, 'msg': '脚本返回数据异常无法解析'}
        
    def update_node(self,k8s_info:K8sNode):
        try:
            update_command= """
import groovy.json.JsonOutput
def res = [:]
try{
def k8s = Jenkins.get().clouds.getByName("%s")
  k8s.setServerUrl(\"%s\")
  k8s.setServerCertificate(\'\'\'%s\'\'\')
  k8s.setSkipTlsVerify(%s)
  k8s.setUseJenkinsProxy(%s)
  k8s.setNamespace(\"%s\")
  k8s.setJnlpregistry(\"%s\")
  k8s.setWebSocket(%s)
  k8s.setDirectConnection(%s)
  k8s.setJenkinsUrl(\"%s\")
  k8s.setConnectTimeout(%s)
  k8s.setReadTimeout(%s)
  k8s.setContainerCapStr(\"%s\")
  k8s.setRetentionTimeout(%s)
  res.code = 1000
  res.msg = "操作成功"
}catch(e){
res.code = 1002
    res.msg = "操作异常:" +e
    }
println(JsonOutput.toJson(res))
"""%(k8s_info.uid,k8s_info.kubernetes_url,k8s_info.kubernetes_cert,("true" if k8s_info.is_https else "false"),
     ("true" if k8s_info.use_proxy else "false"),k8s_info.namespace,k8s_info.jnlp_docker_registry,("true" if k8s_info.is_websocket else "false"),
     ("true" if k8s_info.is_direct else "false"),k8s_info.jenkins_url,k8s_info.connection_timeout,k8s_info.read_timeout,
     k8s_info.max_containter_num,k8s_info.retention_timout
     )
            match self.get_node(k8s_info.uid).get("code"):
                case 1000:
                    return orjson.loads(self.run_script(update_command))
                case _:
                    return self.get_node(k8s_info.name)
        except orjson.JSONDecodeError:
            return {'code': 1002, 'msg': '脚本返回数据异常无法解析'}
        except Exception:
            return {'code': 1002, 'msg': '未知错误'+traceback.format_exc()}


##添加Kubernetes Cloud

k_node = K8sNode(Uid="abcabc",Name="测试集群",KubernetesSecret="Jenkins中已经创建的凭据id",KubernetesUrl="https://1.2.3.4:6443",Namespace="default",JenkinsUrl="http:\\jenkins.demonlg.cn",)
k = K8SCould(url="http://jenkins.demonlg.cn",username="admin",password="xxxxx")
k.add_node(k_node)  #添加K8S集群
k.remove_node(uid="abcabc") #删除K8S集群
k.list() #获取所有Kubernetes Cloud 节点


PS:在Jenkins节点管理页面配置固定节后后,页面会生成一条在agent节点上使用的连接命令,但是在JenkinsApi调用接口生成的时候无法直接生成此命令,如果需要生成此命令需要自行扩展相关内容

import jenkins
class JenkinsPlugins(jenkins.Jenkins):
    def create_agent_command(self, node_name, url, remote_path):
        """
        :param node_name:    节点的名字
        :param url:          这个是agent连接Jenkins的地址 http://, 
        :param remote_path: 这个是生成节点时输入的远程工作目录字段
        :return:
        """

        secret_str = self.run_script(
            f"println(jenkins.model.Jenkins.getInstance().getComputer('{node_name}')?.getJnlpMac())")
        res_info = f"""方法一:
java -jar agent.jar -jnlpUrl {url}/computer/{node_name}/jenkins-agent.jnlp -secret {secret_str} -workDir {remote_path}
方法二:
echo {secret_str} > secret-file
java -jar agent.jar -jnlpUrl {url}/computer/{node_name}/jenkins-agent.jnlp -secret @secret-file -workDir {remote_path}  """
        return res_info if res_info.find("https://") == -1 else res_info.replace("-O", "-sO")

你可能感兴趣的:(Python,DevOps技术分享,python,java,运维开发,devops,运维,自动化)