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.