How to check jenkins node connection status using jenkins-cli - jenkins

I'm trying to check specific Jenkins node is connected using Jenkins CLI
. To get the node details I can use get-node command and it returns details as this xml
<?xml version="1.0" encoding="UTF-8"?>
<slave>
<name>20170602_jenkins_slave_002</name>
<description>10.49.82.46</description>
<remoteFS>c:\\jenkins_root</remoteFS>
<numExecutors>1</numExecutors>
<mode>EXCLUSIVE</mode>
<retentionStrategy class="hudson.slaves.RetentionStrategy$Always"/>
<launcher class="hudson.slaves.JNLPLauncher"/>
<label>20170602_jenkins_slave_002</label>
<nodeProperties/>
<userId>admin</userId>
</slave>
But it does not includes the node status. Anyone has idea how to check the node status by Jenkins cli

As I'm not aware of a method using jenkins-cli, my suggestion is to use the REST API:
http://jenkins.example.com/computer/:name/api/json
returns JSON data including an offline field.
You can get an overview over all agents (called computer here) on the following URL:
http://jenkins.example.com/computer/api/json?pretty=true

We can implement this using pipeline. I did using the groovy script for the windows VDI machines, Below is the script.
pipeline {
agent none
stages {
stage('Agent Health check step') {
options {
timeout(time:5, unit: 'MINUTES')
}
agent {
label "master"
}
steps {
script {
def agStatusList = []
def agentsMap = [VDI1:"XYZ#gmail.com", VDI2:"XYZ2#gmail.com", VDI3:"XYZ#gmail.com", VDI4:"himesh.patel#gmail.com"]
String agEmlTo=''
for (agSlave in hudson.model.Hudson.instance.slaves) {
def agName=agSlave.name
def agOwner=agentsMap.get(agName)
def agStatus=agSlave.getComputer().isOnline()
if (agStatus != true ){
echo "Node is offline"
echo " "
agStatusList += agName + " node is offline" + " and owner is " + agOwner
if(agStatus != true && agOwner != null){
if (agEmlTo == null || agEmlTo.trim().isEmpty()){
agEmlTo = agOwner
} else{
agEmlTo += (','+agOwner)
}
}
}else{
echo "Node is online"
echo " "
agStatusList += agName + " node is online" + " and owner is " + agOwner
}
}
println agStatusList
println agEmlTo
if (agEmlTo != null && !agEmlTo.trim().isEmpty()) {
echo "Sending email to the owners of offline Nodes"
string agEmlCc='123#gmail.com'
string agEmlFrom='JenkinsServer#gmail.com'
String agEmlHdr='[Notification] Your Windows VDI agent is offline'
String agEmlBody='Hello, \n \nThe Windlows VDI agent assigned to you seems offline now. Please check the Windows VDI machine and get it connected ASAP.\n \n *This E-mail is from Automated process. Please do not respond directly to this e-mail as the originating e-mail account is not monitored*'
emailext body: "${agEmlBody}",
to: "${agEmlTo}",
subject: "${agEmlHdr}"
from: "JenkinsServer#gmail.com"
}
}
}
}
}
}

Related

Using hostname to find Nodename or label to connect to Node

I have hostname that is found in a xml file but would need to use that hostname to get the node name in Jenkins to be able to connect to that node and do the scripting.
The hostname is example.co.uk and would like to use script to find the hostname in jenkins and get the node name of the Jenkins and use that as find-hostname. Is this possible?
////
stage('use_nodename_found') {
node("$find-hostname") {
unstash 'db-process'
sh """
sudo su - postgres -c 'echo -e "-------------------------------System Information of current node running --------------------"'
""".stripIndent()
You can use the following script to retrieve the Node name passing the Host. If you want to get the labels instead of node.getNodeName() call node.getLabelString(). Read more here.
def getNodeName(def host) {
for(def node : Jenkins.instance.getNodes()) {
if(node.getLauncher().getHost() == host){
return node.getNodeName()
}
}
return null
}
Note: This is a basic script, make sure you d proper error handling.
Update: Print all Hosts
def getNodeName(def host) {
for(def node : Jenkins.instance.getNodes()) {
println node.getNodeName()
println node.getLauncher().getHost()
}
return null
}
Following is a full scripted Pipeline.
node {
stage('Test') {
echo getNodeName("172.17.0.3")
}
}
def getNodeName(def host) {
for(def node : Jenkins.instance.getNodes()) {
if(node.getLauncher().getHost() == host){
return node.getNodeName()
}
}
return null
}

What's the best way to validate users conformed to requirements of a jenkins pipeline?

I'm attempting to validate that all Jenkins pipelines, at least in a single group/organization, have published their junit tests. Is there a way to programmatically do this? Also, would it be relegated to Jenkinsfiles or work on all pipelines? Thanks!
I could manually check this via looking for the "Test Results" on the page that I have included the image for below. This indicates that the job has published Test Results to the JUnit plugin.
If I were to write a Jenkinsfile, it might look something like this. But it is possible to attach these to the JUnit pipeline via manual methods as well:
pipeline {
agent any
stages {
stage('Compile') {
steps {
// Login to Repository
configFileProvider([configFile(fileId: 'nexus_maven_configuration', variable: 'MAVEN_SETTINGS')]) {
sh 'mvn -s $MAVEN_SETTINGS compile'
}
}
}
stage('Test') {
steps {
configFileProvider([configFile(fileId: 'nexus_maven_configuration', variable: 'MAVEN_SETTINGS')]) {
sh 'mvn -s $MAVEN_SETTINGS test'
}
}
}
}
post {
always {
junit '**/target/surefire-reports/*.xml'
archive 'target/*.jar'
}
}
}
Here is a script you can use to check whether you have tests attached for Jobs in a specific Subdirectory. You can either run this through a Pipeline or using the Jenkins Script Console.
def subFolderToCheck = "folder1" // We will only check Jobs in a specific sub directory
Jenkins.instance.getAllItems(Job.class).each { jobitem ->
def jobName = jobitem.getFullName()
def jobInfo = Jenkins.instance.getItemByFullName(jobName)
// We will check if the last successfull build has any tests attached.
if(jobName.contains(subFolderToCheck) && jobInfo.getLastSuccessfulBuild() != null) {
def results = jobInfo.getLastSuccessfulBuild().getActions(hudson.tasks.junit.TestResultAction.class).result
println("Job : " + jobName + " Tests " + results.size())
if(results == null || results.size() <= 0) {
print("Job " + jobName + " Does not have any tests!!!!!")
}
}
}

Jenkins pipeline - "cannot invoke method on null object" on function outside the pipeline

i get the error above when trying to run my pipeline
tried to run it inside and outside the groovy sandBox.
also tried debugging and it's fails on this method call "last_build_number(lastBuildToGenerateNumber)"
before adding try, catch and recursion this code was working well outside the pipeline. don't get me wrong - this code can not run inside the pipeline so i did not try it.
/*
SEIIc DPS_NIGHTLY_BUILD JenkinsFile
*/
def buildDescription // for setting the build name, based on the downstream jobs name
def last_build_number(build) {
println 'the display name is' + build.displayName
return build.displayName
if (build != null) {
if(build.displayName!=null){
println 'the display name is' + build.displayName
return build.displayName
}
}
else {
return '0.0.0.0'
}
return '0.0.0.0'
}
def autoIncBuildNightlyNumber(build) {
def debugEnable = 1
println 'build is: ' + build.displayName
def lastBuildToGenerateNumber = build; //a build variable
def last_build_number; //last build number i.e: "52.88.0.7" or "#43"
build_number=0;
try{
println 'last build to genreate from ' + lastBuildToGenerateNumber.displayName
last_build_number = last_build_number(lastBuildToGenerateNumber);
if (debugEnable == 1) println 'last successfull build: ' + last_successfull_build
def tokens = last_build_number.tokenize('.')
if (debugEnable == 1) println 'tokens: ' + tokens
// update global variable - if it's not a legit number the crash will be catched
build_number = tokens[3].toInteger() + 1
if (debugEnable == 1) println 'new build number: ' + build_number
return build_number
} catch (e){
if (debugEnable == 1) println 'error is ' + e
if (debugEnable == 1) println 'build number: ' + build_number + ' is not valid. recurse now to find a valid number'
build_number = autoIncBuildNightlyNumber(lastBuildToGenerateNumber.getPreviousBuild());
println 'genrate ' + lastBuildToGenerateNumber
return build_number
}
}
// Declarative Pipeline
pipeline {
/*
maximum time for this job
*/
options { //maximum time for this job
timeout(time: 1, unit: 'HOURS')
}
environment {
AUTO_BUILD_NUMBER = autoIncBuildNightlyNumber(currentBuild.getPreviousBuild())
PLASTICSCM_TARGET_SERVER = "g-plasticscm-server.gilat.com:8087"
PLASTICSCM_TARGET_REPOSITORY = "SEIIc_DPS"
PLASTICSCM_WORKSPACE_NAME = "${env.JOB_BASE_NAME}_${env.BUILD_NUMBER}"
AUTOMATION_FOLDER = "${env.PLASTICSCM_WORKSPACE_NAME}\\Tools\\Automation"
Branch = "/main"
TEST_BRANCH = "/QualiTest for SW Automation"
QUALITEST_FOLDER = "${env.PLASTICSCM_WORKSPACE_NAME}\\QualiTest for SW Automation"
PLASTICSCM_TEST_REPOSITORY="SW_Utiles"
PLASTICSCM_TEST_WORKSPACE = "TEST_${env.JOB_BASE_NAME}_${env.BUILD_NUMBER}"
}
// Select target host for building this pipeline
agent { node { label "SEIIc_DPS" } }
// Stages to run for this pipeline
stages {
/*
Checkout files from source control. In this case the pipeline use PlasticSCM plugin to checkout a branch with given parameter "Branch".
When this stage run, it will checkout the branch in the parameter string from the defined repository and server.
It will not
*/
stage('SCM Checkout') {
steps {
cm branch: env.Branch, changelog: true, repository: env.PLASTICSCM_TARGET_REPOSITORY, server: env.PLASTICSCM_TARGET_SERVER, useUpdate: false, workspaceName: env.PLASTICSCM_WORKSPACE_NAME
//checkOut QualiTest
cm branch: env.TEST_BRANCH, changelog: false, repository: 'SW_Utiles', server: env.PLASTICSCM_TARGET_SERVER, useUpdate: false, workspaceName: env.PLASTICSCM_TEST_WORKSPACE
}
}
}//stages
}//pipeline

Jenkins declarative pipeline: find out triggering job

We have a Jenkins job that uses a declarative pipeline.
This job can be triggered by different other builds.
In the declarative pipeline how can I find out which build has triggered the pipeline?
Code sample below
pipeline {
agent any
stages {
stage('find upstream job') {
steps {
script {
def causes = currentBuild.rawBuild.getCauses()
for(cause in causes) {
if (cause.class.toString().contains("UpstreamCause")) {
println "This job was caused by job " + cause.upstreamProject
} else {
println "Root cause : " + cause.toString()
}
}
}
}
}
}
}
You can check the job's REST API to get extra information like below
{
"_class" : "org.jenkinsci.plugins.workflow.job.WorkflowRun",
"actions" : [
{
"_class" : "hudson.model.ParametersAction",
"parameters" : [
]
},
{
"_class" : "hudson.model.CauseAction",
"causes" : [
{
"_class" : "hudson.model.Cause$UpstreamCause",
"shortDescription" : "Started by upstream project \"larrycai-sto-46908390\" build number 7",
"upstreamBuild" : 7,
"upstreamProject" : "larrycai-sto-46908390",
"upstreamUrl" : "job/larrycai-sto-46908390/"
}
]
},
Reference:
https://jenkins.io/doc/pipeline/examples/#get-build-cause
Get Jenkins upstream jobs
I realize that this is a couple years old, but the previous response required some additional security setup in my Jenkins instance. After a bit of research, I found that there was a new feature request completed in 11/2018 that addresses this need and exposes build causes in currentBuild. Here is a little lib I wrote that returns the cause with the string "JOB/" prepended if the build was triggered by another build:
def call(body) {
if (body == null) {body = {DEBUG = false}}
def myParams= [:]
body.resolveStrategy = Closure.DELEGATE_FIRST
body.delegate = myParams
body()
def causes = currentBuild.getBuildCauses()
if (myParams.DEBUG) {
echo "causes count: " + causes.size().toString()
echo "causes text : " + causes.toString()
}
for(cause in causes) {
// echo cause
if (cause._class.toString().contains("UpstreamCause")) {
return "JOB/" + cause.upstreamProject
} else {
return cause.toString()
}
}
}
To use this, I place it in a library in a file named "buildCause.groovy". Then I reference the library at the top of my Jenkinsfile:
library identifier: 'lib#master', retriever: modernSCM(
[$class: 'GitSCMSource', remote: '<LIBRARY_REPO_URL>',
credentialsId: '<LIBRARY_REPO_CRED_ID', includes: '*'])
Then I can call it as needed within my pipeline:
def cause=buildCause()
echo cause
if (!cause.contains('JOB/')) {
echo "started by user"
} else {
echo "triggered by job"
}
Larry's answer didn't quite work for me.
But, after I've modified it slightly with the help of these docs and this version works:
def causes = currentBuild.getBuildCauses()
for(cause in causes) {
if (cause._class.toString().contains("UpstreamCause")) {
println "This job was caused by job " + cause.upstreamProject
} else {
println "Root cause : " + cause.toString()
}
}
P.S. Actually, Daniel's answer mentions this method, but there's too much clutter, I only noticed it after I wrote my solution.

Get username logged in Jenkins from Jenkins Workflow (Pipeline) Plugin

I am using the Pipeline plugin in Jenkins by Clouldbees (the name was Workflow plugin before), I am trying to get the user name in the Groovy script but I am not able to achieve it.
stage 'checkout svn'
node('master') {
// Get the user name logged in Jenkins
}
Did you try installing the Build User Vars plugin? If so, you should be able to run
node {
wrap([$class: 'BuildUser']) {
def user = env.BUILD_USER_ID
}
}
or similar.
To make it work with Jenkins Pipeline:
Install user build vars plugin
Then run the following:
pipeline {
agent any
stages {
stage('build user') {
steps {
wrap([$class: 'BuildUser']) {
sh 'echo "${BUILD_USER}"'
}
}
}
}
}
Here's a slightly shorter version that doesn't require the use of environment variables:
#NonCPS
def getBuildUser() {
return currentBuild.rawBuild.getCause(Cause.UserIdCause).getUserId()
}
The use of rawBuild requires that it be in a #NonCPS block.
It is possible to do this without a plugin (assuming JOB_BASE_NAME and BUILD_ID are in the environment):
def job = Jenkins.getInstance().getItemByFullName(env.JOB_BASE_NAME, Job.class)
def build = job.getBuildByNumber(env.BUILD_ID as int)
def userId = build.getCause(Cause.UserIdCause).getUserId()
There is also a getUserName, which returns the full name of the user.
This works for me without the Build User plugin:
// get first entry of JSONArray
def buildCause = currentBuild.getBuildCauses()[0]
def buildPrincipal = [type:"unknown", name:""]
if (buildCause._class ==~ /.+BranchEventCause/) {
def branchCause = currentBuild.getRawBuild().getCause(jenkins.branch.BranchEventCause)
buildPrincipal = [type:"branch",name:buildCause.shortDescription]
} else
if (buildCause._class ==~ /.+TimerTriggerCause/) {
def timerCause = currentBuild.getRawBuild().getCause(hudson.triggers.TimerTrigger.TimerTriggerCause)
buildPrincipal = [type:"timer", name:"Timer event"]
} else
if (buildCause._class ==~ /.+UserIdCause/) {
def buildUserCause = currentBuild.getRawBuild().getCause(hudson.model.Cause.UserIdCause)
buildPrincipal = [type:"user", name:buildCause.userId]
} else
// ... other causes
def jobUserId, jobUserName
//then somewhere
wrap([$class: 'BuildUser']) {
jobUserId = "${BUILD_USER_ID}"
jobUserName = "${BUILD_USER}"
}
//then
println("Started By: ${jobUserName}")
We were using this plugin : Build User Vars Plugin. More variables are available.
//Below is a generic groovy function to get the XML metadata for a Jenkins build.
//curl the env.BUILD_URL/api/xml parse it with grep and return the string
//I did an or true on curl, but possibly there is a better way
//echo -e "some_string \c" will always return some_string without \n char
//use the readFile() and return the string
def GetUserId(){
sh """
/usr/bin/curl -k -s -u \
\$USERNAME:\$PASSWORD -o \
/tmp/api.xml \
\$BUILD_URL/api/xml || true
THE_USERID=`cat /tmp/api.xml | grep -oP '(?<=<userId>).*?(?=</userId>)'`
echo -e "\$THE_USERID \\c" > /tmp/user_id.txt
"""
def some_userid = readFile("/tmp/user_id.txt")
some_userid
}
I modified #shawn derik response to get it to work in my pipeline:
stage("preserve build user") {
wrap([$class: 'BuildUser']) {
GET_BUILD_USER = sh ( script: 'echo "${BUILD_USER}"', returnStdout: true).trim()
}
}
Then I can reference that variable later on by passing it or in the same scope as ${GET_BUILD_USER} . I installed the same plugin referenced.
Edit: I re-read the question - the below only gets you the user running the build (which technically is often more interesting), not the one triggering the build in the frontend (be it REST-API or WebUI).
If you have Jenkins impersonation enabled, then I believe the result should be equivalent, otherwise this will only get you the user who owns the jenkins agent on the build machine.
Original answer:
Another way would be to
sh 'export jenkins_user=$(whoami)'
Downside: Linux-dependent, difficult to port across multiple agents in a single build (but then, the auth context may be different on each slave)
Upside: No need to install plugins (which on shared/large Jenkins instances can be tricky)
The Build User Vars Plugin is useful when you are executing the stage on an agent.
The alternative is to use the current build clause (see https://code-maven.com/jenkins-get-current-user), which also works when your stage is set with agent none.
The following code is inspired by Juergen's solution but I added more possible trigger reason and display them in a formatted manner:
String getTriggerReason() {
def buildCause = currentBuild.getBuildCauses()[0]
if (buildCause._class ==~ /.+(BranchEventCause|BranchIndexingCause)/) {
if (env.JOB_BASE_NAME == 'master') {
return 'Triggered by master commit'
} else {
return "Triggered by ${buildCause.shortDescription}"
}
}
if (buildCause._class ==~ /.+TimerTriggerCause/) {
return 'Triggered by timer'
}
if (buildCause._class ==~ /.+BuildUpstreamCause/) {
return "Triggered by build #${buildCause.upstreamBuild}"
}
if (buildCause._class ==~ /.+UserIdCause/) {
def userName = buildCause.userName.replaceFirst(/\s?\(.*/, '')
return "Triggered by user ${userName}"
}
return 'Unknown trigger'
}

Resources