I hit the requirement recently to execute a Jenkins job as part of a VM build within vRA. I wanted to execute the job and monitor it for completion. Once the Jenkins job was complete, I knew the VM was ready for usage so needed the action to wait for success.
To do this I used an ABX action within vRA Cloud, using an extensibility proxy to provide connectivity to a local Jenkins instance.
Step 1 – Configure a Jenkins job with parameter input
Create a Jenkins job which includes parameters. For me, this allowed each Jenkins run to be tailored to a particular VM that vRA was provisioning by allowing inputs such as the VM name, IP, OStype, Disk data etc.
Step 2 – Create an ABX Action in Cloud Assembly
Few Notes:
- jenkins_user is a base 64 string of jenkinsusername:apikey
- All input variables are coming from the event broker [inputs] pipeline, which also include any custom properties inherrited from projects and/or blueprints.
- I am utilising some additional modules in this code as can be seen from the ‘import xxx’. In terms of dependancies on the ABX action, only requests needs to be listed
- If the Jenkins job completes successfully, I return an outputs array back to the VM with the URL of the completed Jenkins job, so if there is ever a problem with the build it can be traced.
- *Disclaimer* – I’m pretty naff at coding so it probably could be written in a much neater way than this!!
import base64 import requests import time import json def handler(context, inputs): #Variable definition for i in inputs['addresses']: vmIp = i[0] print(vmIp) vmName = inputs['resourceNames'][0] print(vmName) #env = inputs['customProperties']['sdlc'] env = 'prod' print(env) osType = inputs['customProperties']['osType'][0:1].upper() + inputs['customProperties']['osType'][1:].lower() print(osType) disks = inputs['customProperties']['diskgrid'] print(disks) print(customer) version = 'v1' role = 'Admin' ANSIBLE_ssm_auto_patch = inputs['customProperties']['ssm_auto_patch'].lower() print(ANSIBLE_ssm_auto_patch) jenkins_url = "https://jenkins.local" jenkins_crumb_url = 'https://jenkins.local/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)' jenkins_job_path = "/job/jenkins_job_name" jenkins_user = "base64placeholder" jenkins_build_params = "/buildWithParameters?VMNAME=" + vmName + "&IPADDR=" + vmIp + "&ENVIRONMENT=" + env + "&VERSION=" + version + "&role=" + role + "&OSTYPE=" + osType jenkins_last_build = "/lastBuild/api/json" jenkins_build_path = jenkins_url + jenkins_job_path + jenkins_build_params jenkins_lastbuild_uri = jenkins_url + jenkins_job_path + jenkins_last_build #Create requests header session = requests.Session() session.trust_env = False session.headers.update({"Authorization" : "Basic " + jenkins_user}) session.headers.update({"Content-Type" : "application/json"}) #Get Crumb response = session.post(jenkins_crumb_url, verify = False, proxies = None) print('Request response code is: ' + str(response.status_code)) crumb = response.text.split(":")[1] print('Crumb: ' + crumb) #Update header with crumb session.headers.update({"Jenkins-Crumb" : crumb}) #Execute POST request using variables response = session.post(jenkins_build_path, verify = False, proxies = None) print('Request response code is: ' + str(response.status_code)) if response.status_code == 201: location = str(response.headers['location']) print('Job Queue Found: ' + location) jenkins_start_path = location + '/api/json' response = session.post(jenkins_start_path, verify = False, proxies = None) i = 0 while i < 20: response = session.post(jenkins_start_path, verify = False, proxies = None) if response.status_code == 200: print(response.text) if b"executable" in response.content: json_content = json.loads(response.content) job_url = json_content['executable']['url'] print("Job Identified: " + job_url) break else: time.sleep(3) i = i + 1 print("Loop ID: ") print(i) #Check job is successful current_build_path = job_url + '/api/json' i = 0 while i < 60: response = session.post(current_build_path, verify = False, proxies = None) json_response = json.loads(response.content) print(json_response['result']) if response.status_code == 200: if json_response['result'] == "SUCCESS": print("JOB COMPLETED " + job_url) break elif json_response['result'] == "FAILURE": raise NameError("JOB FAILED " + job_url) break elif json_response['result'] == "ABORTED": raise NameError("JOB ABORTED " + job_url) break else: i = i+1 time.sleep(10) outputs = {} outputs['customProperties'] = inputs['customProperties'] outputs['customProperties']['jenkins_build'] = job_url return outputs
The code loops every 10 seconds waiting for Jenkins to return ‘JOB COMPLETED’, before closing down.
If you make sure your vRA subscription is set to ‘Blocking’, then the VM will not show as available until the action completes.
This job works incredibly well and could also be written in vRO, which would probably be preferable as you could hold all your credentials in a constants file as secure strings…this is functionality still missing for ABX within vRA Cloud.
References:
https://blogs.vmware.com/management/2020/03/vra-abx-powershell.html