Jump to content
Welcome to our new Citrix community!
  • StyleBook automation using NetScaler Console's NITRO API: A DevOps primer


    Steven Wright
    • Validation Status: Validated
      Assigned To: Steven Wright
      Has Video?: No

    Overview

    Citrix Application Delivery Management (ADM) is a centralised management solution that enables you to operate, upgrade, report on, and troubleshoot all your NetScaler's across a global estate.

    One of Citrix ADM's best features is StyleBooks - declarative descriptions of a desired state written in YAML and consisting of input parameters and components driven by those inputs. A StyleBook causes Citrix ADM to read your current NetScaler configuration, compute the differences between your current and target state, and implement the required settings.

    While you can write StyleBooks yourself, Citrix ADM comes with more than 35 default StyleBooks, covering everything from building a simple HTTP load balancer to all of the content switching, load balancing, and authentication settings needed to support Exchange 2016. Learn about using default StyleBooks and creating custom StyleBooks.

    In this blog post, I focus on simple HTTP load balancers but provide enough detail so you can apply the logic to any StyleBook. Specifically, I'm going to focus on a StyleBook called "lb" with the namespace "com.citrix.adc.stylebooks"which you can find by selecting Applications, Configuration, StyleBooks within Citrix ADM.
     

    image.jpg


    This post is the first in a two-part series that describes how to rapidly create services on your NetScaler's using Citrix ADM StyleBook automation, accelerating your migrations. We'll create 300 load balancers across an estate of several NetScaler's within 15 minutes. This post is aimed at DevOps employees who want to start working with Citrix ADM and need a brief introduction with examples (which you can download here).

    In my second post, I target sysadmins and focus on using preprepared scripts to achieve our goal of creating the 300 load balancers.
     


    Citrix ADM and the NITRO API

    The second best thing about Citrix ADM is that you can access it using API calls, which is how we'e going to automate our StyleBook. You can see more on that below but, you can also find more information here:

    To call your script to log in using the NITRO API, you need an ID and secret. You can create the ID and secret for Citrix ADM service by doing the following steps:

    1. Sign in to the Citrix Cloud portal.
    2. Select Identity and Access Management from the menu.
    3. Select the API Access tab.
    4. Name your Secure Client, and click Create Client.

     

    image.jpg

     



    NITRO API request for a session ID

    After you have the ID and secret, you can sign in and collect a session ID using the following Python commands (this isn't my real ID and secret):

    Python

    import http.client
    import json
    
    id = "f20e7432-7a83-416d-a171-9347b92761f4"
    secret = "7-rotB6QMFZTuOgpPajPBg=="
    
    conn = http.client.HTTPSConnection("adm.cloud.com")
                     
    headers = {
            'isCloud': 'true',
            'Content-Type': 'text/plain',
            'Cookie': ''
      }
    request = {
           "login": {
                  "ID": id,
                  "Secret": secret
           }
    }
    
    payload = json.dumps(request)
    conn.request("POST", "/nitro/v2/config/login", payload, headers)
    response = conn.getresponse()
    data = response.read()
    
    if response.status != 200:
      print("Error:")
      print("       Status code: " + str(res.status))
      print("       Error payload: " + data)
      exit(1)
    
    jsondata = json.loads(data)
    sessionid = jsondata["login"][0]['sessionid']
    print (sessionid)
    
    Output
    ia6bMEB9xn_XUW0Rfy_86zNbKlG1rJgZrd-DM3u3MIM
    
    
    

    NITRO API request using the session ID to retrieve a list of NetScaler identifiers

    Now that you have a session ID token, you can request a list of NetScaler's and find the unique identifier of the instance on which you would like to operate. The session ID is taken from the preceding script.

    Python

    import http.client
    import json
    
    sessionid = "ia6bMEB9xn_XUW0Rfy_86zNbKlG1rJgZrd-DM3u3MIM"
    
    conn = http.client.HTTPSConnection("adm.cloud.com")
    headers = {
            'isCloud': 'true',
            'Content-Type': 'text/plain',
            'Cookie': 'NITRO_AUTH_TOKEN=' + sessionid
        }
    conn.request("GET", "/nitro/v2/config/managed_device", None, headers)
    response = conn.getresponse()
    data = response.read()
    
    if res.status != 200:
      print("Error:")
      print("       Status code: " + str(res.status))
      print("       Error payload: " + data)
      exit(1)
    
    jsondata = json.loads(data)
    #print('\n\nHere is the full JSON output from ADM.\n')
    #print (jsondata["managed_device"])
    print('\n\nHere is a list of ADCs.\n')
    for device in jsondata["managed_device"]:
            name = device['hostname']
            ip = device['ip_address']
            id = device['id']
            type = device['type']
            version = device['version']
            status = device['status']
            if status == "Success":
                    print (name + " " + ip + " " + id + " " + type)
    
    Output
    Here is a list of ADCs.
    myhostname 192.168.1.240 a1fe50be-e187-44bb-871b-d60f112b0aff nsvpx 
    
    
    

    Knowing the parameters to submit to a StyleBook

    Now that we have a session ID and the identifier of an NetScaler to target, we can run a StyleBook. First, however, we need to understand the various parameters that it expects us to submit before doing so.

    You can gather the parameters by programmatically requesting the StyleBook's schema, which describes all valid parameters and which are optional, or by reviewing the source YAML for the StyleBook and its dependencies.

    Here are the Python commands to request a StyleBook's schema.

    Python

    import http.client
    import json
    
    sessionid = "ia6bMEB9xn_XUW0Rfy_86zNbKlG1rJgZrd-DM3u3MIM"
    
    base_types = ["string", "ipaddress", "number", "boolean", "tcp-port", "password", "file", "certfile", "keyfile", "certkey", "ipnetwork"]
    
    def get_config_parameters(parameters):
      configpack_params = {}
      for parameter in parameters:
        parameter_name = parameter["name"]
        parameter_type = parameter["type"]
        parameter_required = parameter["required"]
    
        if parameter_type in base_types:
            if parameter_required:
              parameter_type += "*"
            if parameter_type.endswith("[]"):
              configpack_params[parameter_name] = [parameter_type]
            else:
              configpack_params[parameter_name] = parameter_type
        else:
            if "parameters" in parameter:
              subparameters = parameter["parameters"]
              datatype = get_config_parameters(subparameters)
            else:
              datatype = parameter_type
    
            if parameter_type.endswith("[]"):
              configpack_params[parameter_name] = [datatype]
            else:
              configpack_params[parameter_name] = datatype
    
      return configpack_params
    conn = http.client.HTTPSConnection("adm.cloud.com")
    stylebook = "com.citrix.adc.stylebooks/1.1/lb"
    headers = {
      'Cookie': 'NITRO_AUTH_TOKEN= ' + sessionid
    }
    conn.request("GET", "/stylebook/nitro/v1/config/stylebooks/" + stylebook + "/schema", None, headers)
    res = conn.getresponse()
    data = res.read()
    
    if res.status != 200:
      print("Error:")
      print("       Status code: " + str(res.status))
      print("       Error payload: " + data)
      exit(1)
    
    schema = json.loads(data, "utf-8")
    
    parameters = schema["schema"]["parameters"]
    
    config_parameters = {
      "parameters": get_config_parameters(parameters)
    }
    
    print(json.dumps(config_parameters))
    
    
    

    The output from this schema request is the format of the parameters section of a config pack. From this output, you can remove parameters you don't intend to use. As a clue, the required parameters are marked with a "*" at the end of their type.

    Another (and possibly easier) way to achieve the same thing is to use the Citrix ADM UI to create a template that shows only the parameters I want to use.

    To create the template, I open Citrix ADM's web interface and run my selected StyleBook with dummy values against a non-production NetScaler in a lab. In doing so, I create a config pack with each of the values I would need for a production service.
     

     

    image.jpg


     

     


    Having created the config pack, I now select Applications > Configuration > Config Packs from within the Citrix ADM web interface and export the resultant configuration.
     

    image.jpg

     

     

    image.jpg

     


    After downloading and opening the exported ZIP file, I find that it contains a single JSON file with all the parameters that I need to submit to the API.

     

    image.jpg


     

     



    NITRO API request using the session ID to automate a StyleBook and target an NetScaler identifier

    Finally, having retrieved a session ID, the unique identifier of the NetScaler to operate on, and clarity into the parameters needed to submit to the StyleBook, we are ready to script execution of the StyleBook itself.

    Note: Delete the template configuration pack from Citrix ADM if you want to use the same input parameters within your Python script.

    Python

    import http.client
    import json
    
    sessionid = "ia6bMEB9xn_XUW0Rfy_86zNbKlG1rJgZrd-DM3u3MIM"
    target_adc = "a1fe50be-e187-44bb-871b-d60f112b0aff"
    
    stylebook = "com.citrix.adc.stylebooks/1.1/lb"
    
    conn = http.client.HTTPSConnection("adm.cloud.com")
    payload = json.dumps({
      "configpack": {
        "targets": [
          {
            "id": target_adc
          }
        ],
        "parameters": {
          "lb-appname": "ApplicationName",
          "lb-virtual-ip": "192.168.50.10",
          "lb-virtual-port": "80",
          "lb-service-type": "HTTP",
          "svc-service-type": "HTTP",
          "svc-servers": [
            {
              "ip": "192.168.5.20",
              "port": 80,
              "add-server": True
            },
            {
              "ip": "192.168.5.21",
              "port": 80,
              "add-server": True
            }
          ],
        }
      }
    })
    headers = {
      'Content-Type': 'application/json',
      'Accept': 'application/json',
      'Cookie': 'NITRO_AUTH_TOKEN=' + sessionid
    }
    conn.request("POST", "/stylebook/nitro/v1/config/stylebooks/" + stylebook + "/configpacks", payload, headers)
    res = conn.getresponse()
    data = res.read()
    
    if res.status != 200:
      print("Error:")
      print("       Status code: " + str(res.status))
      print("       Error payload: " + data)
      exit(1)
    
    payload = json.loads(data, "utf-8")
    jobid = payload["configpack"]["job_id"]
    
    print("Configuration Job " + jobid + " has started.")
    
    Output
    Configuration Job 204770189 has started.
    

    Viewing a job_id

    At this point you are able to see the config pack that your script created within the ADM GUI and the resultant configuration on your NetScaler.

     

    image.jpg

     

    If that didn't happen, you can view the details of the "job_id" and understand its status. We can retrieve the outcome of the job by sending a GET request.

    To illustrate, I purposely ran another StyleBook targeted at a non-existent NetScaler ID.

    Python

    
    import http.client
    import json
    
    sessionid = "ia6bMEB9xn_XUW0Rfy_86zNbKlG1rJgZrd-DM3u3MIM"
    jobid = "2476087615"
    
    headers = {
      'Content-Type': 'application/json',
      'Accept': 'application/json',
      'Cookie': 'NITRO_AUTH_TOKEN=' + sessionid
    }
    
    conn = http.client.HTTPSConnection("adm.cloud.com")
    
    job_status = None
    
    while job_status != "completed" and job_status != "failed":
      conn.request("GET", "/stylebook/nitro/v1/config/jobs/" + jobid, None, headers)
      response = conn.getresponse()
      data = response.read()
    
      if response.status != 202:
        print("Error:")
        print("       Status code: " + str(response.status))
        print("       Error payload: " + data)
        exit(1)
    
      payload = json.loads(data, "utf-8")
      job_status = payload["job"]["status"]
    
    config_id = payload["job"]["progress_info"][0]["id"]
    
    if job_status == "completed":
      print("Success: configuration " + config_id + " successully created")
    else:
      print("Error: Failed to create configuration.")
      progress_info = payload["job"]["progress_info"]
      for step in progress_info:
          error_details = step["message"]
          print("        " + json.dumps(error_details))
      exit(1)
    
    Output (failure)
    
    Error: Failed to create configuration.
    "Validating the parameters"
    "Configuration pack process failed. Instance with id g4702445-2286-4b6d-8fb1-c231a0ee17b1 not found on the system."

    Here, we can observe a clear explanation of the issue. We can observe success messages on jobs that run without issue.

    Output (success)
    
    Success: configuration 204770189 successfully created
    
    

    Next steps

    We have now seen how we can programmatically authenticate to Citrix ADM, retrieve a list of identifiers for the NetScaler's that it manages, prepare and run StyleBooks against a chosen NetScaler, and retrieve the result.
     

    We have also observed how you can find the namespace, version, and name of alternative StyleBooks and retrieve the various properties they require.
     

    Using this knowledge, your DevOps team is able to integrate Citrix ADM StyleBooks and their automated NetScaler configuration into your existing workflows.
     

    In the second of this two-part series, using the example of creating 300 load balancers across an estate of several NetScaler's within 15 minutes, I share prebuilt scripts that allow those without a DevOps team to create business services rapidly during a platform migration.


    User Feedback

    Recommended Comments

    There are no comments to display.



    Create an account or sign in to comment

    You need to be a member in order to leave a comment

    Create an account

    Sign up for a new account in our community. It's easy!

    Register a new account

    Sign in

    Already have an account? Sign in here.

    Sign In Now

×
×
  • Create New...