Creating a tmsh script with iControl REST and using it to restart HTTPD
Problem this snippet solves:
TMSH has the ability to create tcl scripts that can be used to run multiple commands and transactions. It is rare that you will want to create on with TMSH but there are a few cases where this may be desirable. One of these is to restart HTTPD, which is difficult to do from iControl REST because the REST API is running over HTTPD and the restart will not be clean. See K13292945.
This Python script creates a tmsh script, and then runs it to restart HTTPD.
How to use this snippet:
Syntax is <program_name.py> host user password
Sample output:
./rest_script_example.py 10.155.117.12 admin admin Before httpd (pid 3186) is running... After httpd (pid 3289) is running...
Code :
#!/usr/bin/python #m.lloyd@f5.com #Makes tmsh script to restart HTTP #Syntax:host username password import json #allow python 2 and python 3 by loading the correct libraries. try: from http.client import BadStatusLine from urllib.parse import urlparse, urlencode from urllib.request import urlopen, Request from urllib.error import HTTPError except ImportError: from httplib import BadStatusLine from urlparse import urlparse from urllib import urlencode from urllib2 import urlopen, Request, HTTPError import ssl import sys import time #Internal calls will not verify certs so disable cert verification. ssl._create_default_https_context = ssl._create_unverified_context #Create request for token based authentication. This is in Bigip 12 and later: url = 'https://'+sys.argv[1]+'/mgmt/shared/authn/login' values = {'username' : sys.argv[2], 'password' : sys.argv[3], 'loginProviderName' : 'tmos'} values = json.dumps(values).encode('utf-8') Request(url,data=values) req = Request(url,data=values) req.add_header('Content-Type' , 'application/json') #Request authentication token. response = urlopen(req) #auth=result will be a json data structure. auth_result = response.read() #print (auth_result) #Json.loads makes an internal python data structure that is easier to extract auth token from json. #Now construct icontrol rest query for device-groups info. auth=json.loads(auth_result) token=(auth['token']['token']) #print(token) #Get current PID of HTTPD url = 'https://'+sys.argv[1]+'/mgmt/tm/sys/service/httpd/stats' req = Request(url) req.add_header('X-F5-Auth-Token',auth['token']['token']) response = urlopen(req,data=None) json_response=(response.read()) python_response=json.loads(json_response) print("Before") print(python_response["apiRawValues"]["apiAnonymous"]) #look for script with name to make sure that the script does not already exist url = 'https://'+sys.argv[1]+'/mgmt/tm/cli/script/example.tcl' #urllib2 raises an exception with an HTTP 404 req = Request(url) req.add_header('X-F5-Auth-Token',auth['token']['token']) try: response = urlopen(req,data=None) except HTTPError as err: if err.code==404: #print (err.code) #print("\nCreate cli script\n") #request create here url = 'https://'+sys.argv[1]+'/mgmt/tm/cli/script' req = Request(url) req.add_header('X-F5-Auth-Token',auth['token']['token']) req.add_header('Content-Type' , 'application/json') values = {"name":"example.tcl", "apiAnonymous": "proc script::init {} {\n}\n\nproc script::run {} {\n tmsh::run util bash -c 'killall -9 httpd' \n tmsh::start sys service httpd\n} \n\nproc script::help {} {\n}\n\nproc script::tabc {} {\n}\n"} values = json.dumps(values) response = urlopen(req,data=values) response_py=(json.load(response)) #print(json.dumps(response_py,sort_keys=True,indent=4)) #Now run script url = 'https://'+sys.argv[1]+'/mgmt/tm/cli/script/example.tcl' req = Request(url) req.add_header('X-F5-Auth-Token',auth['token']['token']) req.add_header('Content-Type' , 'application/json') values = {"kind":"tm:cli:script:runstate","command":"run"} values = json.dumps(values).encode('utf-8') # Killing HTTPD will abort the connection so catch the exception. try: urlopen(req,data=values) except BadStatusLine: pass #Wait for httpd to restart so you can query. time.sleep(5) #Get current PID of HTTPD after restart url = 'https://'+sys.argv[1]+'/mgmt/tm/sys/service/httpd/stats' req = Request(url) req.add_header('X-F5-Auth-Token',auth['token']['token']) response = urlopen(req,data=None) json_response=(response.read()) python_response=json.loads(json_response) print("After") print(python_response["apiRawValues"]["apiAnonymous"])
Tested this on version:
13.0Great thanks! I needed to run a bash command but I decided to go with "util/bash" API endpoint (https://community.f5.com/t5/technical-forum/running-bash-commands-via-rest-api/td-p/272516 ) but now I know I can also run bash commands from a tmsh script 🙂
The only thing with such scripts is how to see the real output when using API:
curl -sku admin:niki@111 https://10.1.1.130/mgmt/tm/cli/script/niki -H "Content-Type: application/json"
Output API:
{"kind":"tm:cli:script:scriptstate","name":"niki","fullPath":"niki","generation":7832,"selfLink":"https://localhost/mgmt/tm/cli/script/niki?ver=16.1.3.2","apiAnonymous":"proc script::init {} {\n}\n\nproc script::run {} {\nreturn [tmsh::list ltm pool]\n}\n\nproc script::help {} {\n}\n\nproc script::tabc {} {\n}\n","ignoreVerification":"false","totalSigningStatus":"not-all-signed","verificationStatus":"none"}Output CLI:
root@(bigip2)(cfg-sync Standalone)(Active)(/Common)(tmos)# run cli script niki
ltm pool Fake-ICAP-pool {
members {
1.1.1.1:icap {
address 1.1.1.1
}
}Script:
root@(bigip2)(cfg-sync Standalone)(Active)(/Common)(tmos)# edit cli script niki
modify script niki {
proc script::init {} {
}proc script::run {} {
puts [tmsh::list ltm pool]
}proc script::help {} {
}proc script::tabc {} {
}