0 0

Ask Rick: Setting Data IPs on Tintri VMstore

Rick Ehrhart is Tintri’s Developer Evangelist, Engineering. In his column, Rick solves IT problems with one simple solution: Tintri storage. Got a question? Let us know.


Hi Rick,

I am a follower of your development blogs and find it really helpful in Tintri automation. We are trying to automate Tintri provisioning and I am stuck at configuring data IP with rest API. I don’t find post method to configure IP on documentation though get method is available ([v310/appliance/%7buuid%7d/ips/index.html]/v310/appliance/{uuid}/ips) to list ips.

Data IP can be configured with Tintri Automation PowerShell Toolkit, but I prefer REST API. Do you have plan to allow data IP configuration with REST API anytime soon?

Thanks,
Gaurav


Greetings,

Setting the data IP configuration is similar to setting DNS, that is the PUT /v310/appliance/default API is used.

I wrote an example, set_data_ip.py, which displays the current IP addresses, adds a data IP address, or deletes a data IP address.


[rle@rle-vm tintri-api-examples]$ ./set_data_ip.py -h
usage: set_data_ip.py [-h] [--add ADD] [--delete DELETE]
                      server_name user_name password

Optionally adds or deletes a data IP on a VMstore

positional arguments:
  server_name           VMstore server name
  user_name             VMstore user name
  password              User name password

optional arguments:
  -h, --help            show this help message and exit
  --add ADD, -a ADD     Add the specifed IP to the data IP configuration.
  --delete DELETE, -d DELETE
                        Delete the specifed IP from the data IP configuration.

Instead of obtaining DNS addresses as in set _dns _primary.py, this code obtains the current VMstore IP addresses using GET /v310/appliance/default/ips with the get_ip_configs function.


APPLIANCE_URL = "/v310/appliance/default"

# Return IP addresses.
def get_ip_configs(server_name, session_id):
    url = APPLIANCE_URL + "/ips"

    try:
        # Make the get call
        r = tintri.api_get(server_name, url, session_id)
        print_debug("The JSON response of the get invoke to the server " +
                    server_name + " is: " + r.text)

    except tintri.TintriRequestsException as tre:
        message = HTTP error for the get IP addresses invoke to the server."
        raise tintri.TintriApiException(message, r.status_code, url, str(Request), r.text)
    except tintri.TintriApiException as tae:
        message = "The HTTP response for the get IP addresses invoke to the server is not 200."
        raise tintri.TintriApiException(message, r.status_code, url, str(Request), r.text)

    ip_configs = r.json()
    return ip_configs

As a reminder, the appliance API requires a UUID, and "default" is used when invoking the API on the VMstore. We could use the "GET /v310/appliance/default", but "GET /v310/appliance/default/ips" is faster, because less information is retrieved.

One big difference between set _dns _primary.py and set_data_ip.py is that set_dns_primary.py sets multiple VMstores while set_data_ip.py only set one VMstore. This simplifies the code, but it still validates the server to be a VMstore.


try:
    r = tintri.api_version(server_name)
    json_info = r.json()
    if json_info['productName'] != "Tintri VMstore":
        message = "Server needs to be a VMstore"
        raise tintri.TintriRequestsException(message)

Since this example can have different update content, the code separated invoking the PUT v310/appliance/defautlt API from creating the content. Invoking the API is done in update_data_ip(), while creating the update content is done in add_data_ip() and delete_data_ip() depending the which command line option is used.

The update_data_ip() function below takes new_ip_configs which represents the configIps DTO as input. The Request DTO contains the objects with values and properties to be updated. In this case the object with the data IP address values is new_appliance and the property to be modified is configIps. Again, this is similar to process_vmstore() in set_dns_primary.py.


# Set the data IP with new IP configuration.
def update_data_ip(server_name, session_id, new_ip_configs):

    # Create the Appliance object with the new configIps DTO.
    new_appliance = \
        {'typeId': 'com.tintri.api.rest.v310.dto.domain.Appliance',
         'configIps': new_ip_configs
        }

    # Create the Request object with the Appliance DTO.
    Request = \
        {'typeId': 'com.tintri.api.rest.v310.dto.Request',
         'objectsWithNewValues': newAppliance,
         'propertiesToBeUpdated': ['configIps']
        }
    # Update the VMstore wit the new data IP configuration.
    url = APPLIANCE_URL
    r = tintri.api_put(server_name, url, Request, session_id)
    print_debug("The JSON response of the get invoke to the server " +
                server_name + " is: " + r.text)

    # if HTTP Response is not 204 then raise exception.
    if r.status_code != 204:
        message = "The HTTP response for put call to the server is not 204."
        raise tintri.TintriApiException(message, r.status_code, url, str(Request), r.text)

First, let's look at add_data_ip(). Since PUT v310/appliance/default updates data IP addresses, we only want to find data IP addresses in the current list of configured IP addresses. For each data IP addresss, the code appends it to a new list. Also a data IP address configuration is saved to copy later to use as a template for a new data IP address. After the loop, a new data IP address is created by modifying the saved data IP address configuration. Note that the VLAN ID is marked untagged. If your network is using VLANs, this value would be a VLAN ID instead of untagged. The new data IP address configuration is appended to the list of configured IP addresses and the list is returned to the caller.


# Add a data IP to the list of current IPs and return new list.
def add_data_ip(ip_configs, new_data_ip):

    new_ip_configs = []

    # Find a data IP config to copy and copy only the data IP configs.
    for ip_config in ip_configs:
        if (ip_config['ip'] == new_data_ip):
            message = new_data_ip + " already configured."
            raise tintri.TintriRequestsException(message)

        if (ip_config['serviceType'] == "data"):
            ip_config_save = ip_config
            new_ip_configs.append(ip_config)

    if (not ip_config_save):
        message = "Data IP conifg does not exist."
        raise tintri.TintriRequestsException(message)

    data_ip_config = ip_config_save.copy()

    # Modify the save copy for our purposes.
    data_ip_config['ip'] = new_data_ip
    data_ip_config['vlanId'] = "untagged"  # For example only.

    new_ip_configs.append(data_ip_config)
    return new_ip_configs

To delete an IP address, a new data IP address configuration list is again needed. As you can see, it is simplier than the add. The code runs through the configuration list and doesn't append the IP address configuration that matches the specified IP address to the new list of data IP address configuration.


def del_data_ip(ip_configs, data_ip_to_del):

    new_ip_configs = []

    # Append IP config if the data IP doesn't match.
    for ip_config in ip_configs:
        if (ip_config['serviceType'] == "data"):
            if (ip_config['ip'] != data_ip_to_del):
                new_ip_configs.append(ip_config)

    return new_ip_configs

Now onto the final bits of code. The current IP address configuration is always printed. If the add or delete option was specified then the specified work is executed and the new IP address configuration is printed.


# Execute
try:
    ip_configs = get_ip_configs(server_name, session_id)
    print_ip_configs(ip_configs, "Current")

    if add_ip or delete_ip:
        if add_ip:
            new_ip_configs = add_data_ip(ip_configs, new_data_ip)
        if delete_ip:
            new_ip_configs = del_data_ip(ip_configs, new_data_ip)

        update_data_ip(server_name, session_id, new_ip_configs)

        # Display the changes
        ip_configs = get_ip_configs(server_name, session_id)
        print_ip_configs(ip_configs, "New")

except tintri.TintriRequestsException as tre:
    print_error(server_name + ": " + tre.__str__())
except tintri.TintriApiException as tae:
    print_error(server_name + ": " + tae.__str__())

Remember that the Tintri GitHub site has more examples for your coding experience.

Keep the questions coming,

   - Rick -

Rick Ehrhart / Aug 03, 2016

Rick Ehrhart is Tintri’s Developer Evangelist with a strong software development background focusing on APIs. Rick has assisted many companies with API integrations including VMware, NetApp, HP, ...more

Temporary_css