Patrick B.
Patrick B.

Reputation: 12383

How to get a list of all Jenkins nodes assigned with label including master node?

I'm creating a Jenkins pipeline job and I need to run a job on all nodes labelled with a certain label.

Therefore I'm trying to get a list of node names assigned with a certain label. (With a node I can get the labels with getAssignedLabels())

The nodes-list in jenkins.model.Jenkins.instance.nodes seems not contain the master-node which I need to include in my search.

My current solution is to iterate over the jenkins.model.Jenkins.instance.computers and use the getNode()-method to get the node. This works, but in the javadoc of Jenkins I'm reading the this list might not be up-to-date.

In the long-run I will add (dynamically) cloud-nodes and I'm afraid that I won't be able to use computers then.

What is the right way to get the list of all current nodes?

This is what I'm doing right now:

@NonCPS
def nodeNames(label) {
    def nodes = []
    jenkins.model.Jenkins.instance.computers.each { c ->
        if (c.node.labelString.contains(label)) {
            nodes.add(c.node.selfLabel.name)
        }
    }   
    return nodes
}

Upvotes: 29

Views: 64729

Answers (11)

carl verbiest
carl verbiest

Reputation: 1295

I combined the answers from the original question and https://stackoverflow.com/a/54145233/1817610 and saved it as a shared library method

vars/nodeNames.groovy

def call(String label) {
    def nodes = []
    jenkins.model.Jenkins.instance.computers.each { c ->
        c.node.labelString.split(/\s+/).each { l ->
            if (l != null && l.equals(label)) {
                nodes.add(c.node.selfLabel.name)
             }
        }
    }
    return nodes
}

Once the shared library is configured, see https://www.jenkins.io/doc/book/pipeline/shared-libraries/, this can be used as

@Library("my-jenkins-shared-lib") _

print ("linux pipeline can run on" + nodeNames("linux"))

Upvotes: 0

Yakir GIladi Edry
Yakir GIladi Edry

Reputation: 2851

Another wat to get Labels and Display Name of nodes

def jenkins = Jenkins.instance
def computers = jenkins.computers
computers.each {
   println "${it.displayName} ${it.hostName}"
}

def labels = jenkins.getLabels()
labels.each {
   println "${it.displayName}"
}

Upvotes: 0

Patrick B.
Patrick B.

Reputation: 12383

This is the way I'm doing it right now. I haven't found anything else:

@NonCPS
def hostNames(label) {
  def nodes = []
  jenkins.model.Jenkins.get().computers.each { c ->
    if (c.node.labelString.contains(label)) {
      nodes.add(c.node.selfLabel.name)
    }
  }
  return nodes
}

jenkins.model.Jenkins.get.computers contains the master-node and all the slaves.

Upvotes: 18

ingyhere
ingyhere

Reputation: 13841

This is one of the top Google hits for how to list nodes on a Jenkins server. If you're just looking for the list of nodes, it can be viewed at the following server URL:

http://JENKINS_HOSTNAME:JENKINS_PORT/computer/

The result is a table displaying the name, OS, JVM version, clock sync status, remoting version and response time of known nodes. It also displays whether the node image (well, JAR) is significantly outdated or subject to error/security alerts.

If there is no access available to API calls, the URL can always be scraped and parsed to get the list of nodes and any of the other data included in the table.

Upvotes: -2

GManz
GManz

Reputation: 1669

using nodesByLabel as pointed out by @towel is probably the solution in most cases. One limitation I found with nodesByLabel is that there is no way to indiscriminately select all nodes. I can't use any of the other solutions either because of script security, some of these can be pretty dangerous, so I preferred not to approve them for usage.

As an alternatively, you can add a function as a pipeline library, which will allow usage of these functions. Since pipeline libraries can be set up so they are fully under the administrator's control, it's safer to go with this route. To do so, set up a pipeline library (I don't think it matters if it's global or not, but for me it is). Then add the following contents to the file vars/parallelRunOnNodes.groovy:

def call(Closure callback) {
    parallel jenkins.model.Jenkins.get().computers.collectEntries { agent ->
        def nodeLabel = agent.node.selfLabel.name
        ["${nodeLabel}": {
            node("${nodeLabel}") {
                stage("${nodeLabel}") {
                    callback(nodeLabel)
                }
            }
        }]
    }
}

Which can then be used as follows:

pipeline {
    agent none
    stages {
        stage('Parallel on all nodes') {
            steps {
                parallelRunOnNodes { nodeLabel ->
                    println(nodeLabel)
                }
            }
        }
    }
}

Obviously adjust as you see fit, e.g. you can add additional parameters to filter, maybe you don't care about parallel etc.

Upvotes: 2

Andy
Andy

Reputation: 444

Here is my answer

String labelIWantServersOf = "XXXX"; // This is the label assosiated with nodes for which i want the server names of
List serverList = [];

for (aSlave in hudson.model.Hudson.instance.slaves) {          
  if (aSlave.getLabelString().indexOf(labelIWantServersOf ) > -1) {
     if(!aSlave.getComputer().isOffline() ){
          serverList.add(aSlave.name);        
     }
  }    
}

return serverList;

Upvotes: 0

Boris
Boris

Reputation: 24453

Here is a functional solution which is more readable and concise:

def nodes = jenkins.model.Jenkins.get().computers
  .findAll{ it.node.labelString.contains(label) }
  .collect{ it.node.selfLabel.name }

You can verify it in the Jenkins Script Console.

Upvotes: 9

Russell Gallop
Russell Gallop

Reputation: 1699

I think that you can do this with:

def nodes = Jenkins.get.getLabel('my-label').getNodes()
for (int i = 0; i < nodes.size(); i++) {
    node(nodes[i].getNodeName()) {
        // on node
    }
}

I don't know for sure whether this works with cloud nodes.

Upvotes: 2

k4cy
k4cy

Reputation: 334

Update to @patrick-b answer : contains can be buggy if you have labels containing same string, I've added a split step do check every label separated with spaces.

@NonCPS
def hostNames(label) {
    def nodes = []
    jenkins.model.Jenkins.get.computers.each { c ->
        c.node.labelString.split(/\s+/).each { l ->
            if (l != null && l.equals(label)) {
                nodes.add(c.node.selfLabel.name)
             }
        }
    }

    return nodes
}

Upvotes: 8

towel
towel

Reputation: 2214

Updated answer: in a pipeline use nodesByLabel to get all nodes assigned to a label.

Upvotes: 20

kirkpatt
kirkpatt

Reputation: 623

Try using for (aSlave in hudson.model.Hudson.instance.slaves) {} and aSlave.getLabelString()); to get all the labels for all of your nodes. You can construct a list of nodes per label this way.

Upvotes: 1

Related Questions