Forum Discussion

Tim_18689's avatar
Tim_18689
Icon for Cirrus rankCirrus
Apr 20, 2018

Delete and Service from iWorkflow when BIG-IP is not reachable

How to delete an ASO from an iWorkflow when the Placement is in status "ERROR_IN_DELETION"? In version 2.3.0 iWorlflow will not let you delete the service until you "Update the service by issuing a PUT or a PATCH to bring the service state out of ERROR_IN_DELETION". I put the above in quotes because I copied it from the "Remove service" section of: https://devcentral.f5.com/wiki/iWorkflow.APIRef_cm_cloud_tenants_tenant-name_services_iapp.ashx

 

I found that the directions are not very complete and almost impossible to follow. It seems like they do not work unless you are able to delete the Service very quick after changing the status. This is very hard to manually. I have already solved this problem by writing a script but I am posting this question here so I can make it available to others that may be hitting this problem.

 

If you are hitting this issue you can get the script from github:

 

https://github.com/tthomas0702/iwf_aso_delete

 

    • Tim_18689's avatar
      Tim_18689
      Icon for Cirrus rankCirrus
      Here is the script:
      !/usr/bin/python
      
      """
      This is designed to be run locally on iWorkflow to remove stuck ASO in
      "ERROR_IN_DELETION" status. See "Remove Service" at:
      https://devcentral.f5.com/wiki/iWorkflow.APIRef_cm_cloud_tenants_tenant-name_services_iapp.ashx
      Usage:
          ./delete-error.py -u username -p password
      """
      
      import json
      import httplib
      from base64 import b64encode
      import optparse
      import sys
      import socket
      
      parser = optparse.OptionParser()
      
      
       debug option for args
      parser.add_option(
          '-a',
          '--address',
          dest="address",
          default="127.0.0.1",
          action="store",
          help="address of remote device"
          )
      
      parser.add_option(
          '-d',
          '--debug',
          dest="debug",
          default=False,
          action="store_true",
          help="Print out arg values given"
          )
      
      
      parser.add_option(
          '-u',
          '--user',
          dest="remUser",
          default='admin',
          action="store",
          help="Remote user name"
          )
      
      parser.add_option(
          '-p',
          '--pass',
          dest="remPass",
          default='admin',
          action="store",
          help="Remote user name"
          )
      
      
      options, remainder = parser.parse_args()
      
      
       get auth token
      def get_token(user, password, address, debug=False):
          conn = httplib.HTTPSConnection(address)
          cred = user + ":" + password
          userAndPass = b64encode(cred).decode("ascii")
          url = '/mgmt/shared/authn/login'
          post_data = '{"username":"' + user + '","password":"' + password + '"}'
          headers = {
              'Authorization': 'Basic %s' % userAndPass,
              'Content-Type': 'application/json'}
          try:
              conn.request("POST", url, post_data, headers=headers)
              r1 = conn.getresponse()
              if r1.status != 200:
                  print r1.status, r1.reason
                  sys.exit(0)
              data = r1.read()
          except socket.error, e:
              print e
              sys.exit(0)
          except:
              raise
          conn.close()
          data_dict = json.loads(data)
          token = data_dict['token']['token']
          return token
      
      
       GET
      def get(address, url, auth_token, debug=False):
          conn = httplib.HTTPSConnection(address)
          headers = {
              'X-F5-Auth-Token': auth_token, 'Content-Type': 'application/json'}
          try:
              conn.request("GET", url, headers=headers)
              r1 = conn.getresponse()
              if r1.status != 200:
                  print r1.status, r1.reason
                  sys.exit(0)
              data = r1.read()
          except socket.error, e:
              print e
              sys.exit(0)
          except:
              raise
          conn.close()
          data_dict = json.loads(data)
          get_data = data_dict
          return get_data
      
      
       PUT
      def put(address, url, auth_token, put_data, debug=False):
          conn = httplib.HTTPSConnection(address)
          headers = {
              'X-F5-Auth-Token': auth_token, 'Content-Type': 'application/json'}
          try:
              conn.request("PUT", url, put_data, headers=headers)
              r1 = conn.getresponse()
              if r1.status != 200:
                  print r1.status, r1.reason
                  sys.exit(0)
              data = r1.read()
          except socket.error, e:
              print e
              sys.exit(0)
          except:
              raise
          conn.close()
          data_dict = json.loads(data)
          put_result = data_dict
          return put_result
      
      
       post
      def post(address, url, auth_token, post_data, debug=False):
          conn = httplib.HTTPSConnection(address)
          headers = {
              'X-F5-Auth-Token': auth_token, 'Content-Type': 'application/json'}
          try:
              conn.request("POST", url, post_data, headers=headers)
              r1 = conn.getresponse()
              if r1.status != 200:
                  print r1.status, r1.reason
                  sys.exit(0)
              data = r1.read()
          except socket.error, e:
              print e
              sys.exit(0)
          except:
              raise
          conn.close()
          data_dict = json.loads(data)
          post_result = data_dict
          return post_result
      
      
       PATCH
      def patch(address, url, auth_token, patch_data, debug=False):
          conn = httplib.HTTPSConnection(address)
          headers = {
              'X-F5-Auth-Token': auth_token, 'Content-Type': 'application/json'}
          try:
              conn.request("PATCH", url, patch_data, headers=headers)
              r1 = conn.getresponse()
              if r1.status != 200:
                  print r1.status, r1.reason
                  sys.exit(0)
              data = r1.read()
          except socket.error, e:
              print e
              sys.exit(0)
          except:
              raise
          conn.close()
          data_dict = json.loads(data)
          patch_result = data_dict
          return patch_result
      
      
      def delete(address, url, auth_token, debug=False):
          conn = httplib.HTTPSConnection(address)
          headers = {
              'X-F5-Auth-Token': auth_token, 'Content-Type': 'application/json'}
          try:
              conn.request("DELETE", url, headers=headers)
              r1 = conn.getresponse()
              if r1.status != 200:
                  print r1.status, r1.reason
                  sys.exit(0)
              data = r1.read()
          except socket.error, e:
              print e
              sys.exit(0)
          except:
              raise
          conn.close()
          return data
      
      
      if __name__ == "__main__":
      
           setup vars
          user = options.remUser
          password = options.remPass
          address = options.address
          debug = options.debug
      
           print arg info in debug
          if options.debug:
              print "options dict: ", options, " remainder: ", remainder
      
           get auth token
          auth_token = get_token(user, password, address, debug)
          if options.debug:
              print "auth token is: ", auth_token
      
           GET list of placemnts in "status": "ERROR_IN_DELETION"
          url = '/mgmt/cm/cloud/tenants/services/placements/'
          get_placements = get(address, url, auth_token, debug)
          placements_list = get_placements['items']
          for p in placements_list:
              if p['status'] == 'ERROR_IN_DELETION':
                  print "Placement ", p['appName'], " in status:"
                  print "status ", p['status']
                  print "For L4-L7 Service:"
                  print p['tenantServiceInstance']['selfLink']
      
                   doing a PUT on L4-L7 Service with the body of itself
                   changes placement status to ERROR_IN_PLACEMENT
                   then we can delete the L4-L7 Service
                  url = p['tenantServiceInstance']['selfLink'][17:]
      
                   GET service json for PUT body
                  service = get(address, url, auth_token, debug)
      
                   format of PUT body
                  put_data = json.dumps(service, indent=4)
      
                   Do PUT on the service to change placement status
                  put_stat = put(
                      address, url, auth_token, put_data, debug=False)
      
                   delete service right after, seems to only work if done quickly
                   I could not get to work by manually doing this with curl
                   I do not know the reason for this
                  print "Deleting ", p['appName'], "\n"
                  delete_stat = delete(
                      address, url, auth_token, debug=False)