Creating custom Prism Central categories with PHP

Creating custom categories with PHP

In a recent discussion with Frederic Lhoest, one of our highly knowledgeable and experienced customers, a question came up regarding the management of Prism Central categories. Given the nature of the Frederic’s requirements, he had created a custom package for use with PHP applications, allowing quick access to the Nutanix APIs via that package.

The idea itself isn’t uncommon, i.e. creating custom packages to streamline tasks, especially automation tasks. With the idea in mind, though, I thought I’d throw together a quick example of how I would do this with PHP. The results are also available as code samples on Nutanix.dev – PHP and Python examples are available. I hope they help someone! The GitHub repository referenced through this article can be found on the NutanixDev GitHub.

The original blog posts written by Frederic, in which he outlines his requirements and steps are available here – thanks for permission to link and cross-post, Frederic! 🙂

Categories intro

Before getting into the demo any further, let’s quickly take a look at what Prism categories are:

A category is a grouping of entities into a key value pair. Typically, new entities are assigned to a category based on some criteria. Policies can then be tied to those entities that are assigned (grouped by) a specific category value.

For example, you might have a Department category that includes values such as engineering, finance, and HR. In this case you could create one backup policy that applies to engineering and HR and a separate (more stringent) backup policy that applies to just finance. Categories allow you to implement a variety of policies across entity groups, and Prism Central allows you to quickly view any established relationships.

Nutanix Prism Virtual Infrastructure (Cluster) Administration documentation

As you can see, categories form a key part of not only configuring but also securing and controlling many aspects of the Prism platform.

Environment requirements

To follow along with this article, you’ll need to have an environment that meets certain requirements. I’ve outlined them below, along with links to the various installation instructions that go with each package, if available.

  • Linux, Mac or Windows PC capable of running PHP >= 7.2
    • My current development is Ubuntu 20.04 and PHP version 7.4.16
  • A connection to a Nutanix Prism Central instance. I’m currently running Prism Central pc.2021.1.0.1, the latest version as I write this.
  • You will be making changes to the Prism Central configuration and, as a result, will need to access Prism Central with administrative privileges.
  • An installed and working version of PHP Composer, the pretty much ubiquitous PHP package manager. Please see the PHP Composer website for installation documentation.
  • A local install of <a rel="noreferrer noopener" href="https://git-scm.com/downloads" target="_blank">git</a> – required to clone the repository from GitHub.

Demo requirements

The requirements for this demo are simple and designed in a way that allows easy explanation via blog article. The way I did this is a little different from the way the customer did, although the end results are quite similar. My requirements boiled down to a single “parent” requirement:

  • The demo code actions must be customisable without the need to modify the code.
    • The demo must support the specification of category names via a separate JSON file.
    • The demo must support the configuration of Prism Central details (IP address and credentials) without hard-coding any of those details into the code itself.

Using the demo

To grab a copy of this demo code and use it yourself, the steps are straightforward. Follow along below and you’ll be up and running in only a few minutes.

Environment preparation

Before running the demo, I suggest creating a directory structure for your projects. If you already have one, please feel free to use that. The following commands are executed in a Linux/Mac terminal or Windows command prompt.

mkdir projects
cd projects

Clone GitHub repository

With our projects directory created, we can clone the demo source from the NutanixDev GitHub repository:

git clone https://github.com/nutanixdev/php-categories.git
cd php-categories

Configuring demo

The demo script includes a sample JSON file named categories.json and an environment configuration file named .env.example. To use the demo script, you’ll need to edit both these files, as follows:

  • Edit categories.json so that it matches the categories keys and values you’d like to create
  • Rename .env.example to .env and edit it to contain your environment credentials. It is worth noting that many code repositories explicitly exclude files named .env from source control so that secure credentials are not accidentally pushed to public source control.

Installing dependencies

This demo script is also supplied with a file named composer.json. This file contains a list of the script’s dependencies, that is, the PHP packages that must be present before the script will execute successfully. If this isn’t done before executing the demo script, the resulting error will tell you exactly what happened:

Attempting to execute the demo script without installing dependencies

However, if we take a look at composer.json, we can get a better idea of what is required for the script to work.

{
    "name": "nutanix/pc_category_management",
    "description": "Create Nutanix Prism Central categories keys and values based on JSON file",
    "require": {
        "php": ">=7.2",
        "guzzlehttp/guzzle": "^7.2",
        "vlucas/phpdotenv": "^5.2"
    },
    "license": "MIT"
}

As you can see, three packages have been specified as required for the script to run:

  1. “php” version of 7.2 or later.
  2. “guzzlehttp/guzzle” version 7.2 or later, but earlier than 8.0. This package manages our API requests.
  3. “vlucas/phpdotenv” version 5.2 or later, but earlier than 6.0. This package allows us to read environment configuration from a file named .env (by default).

For more information on what can be defined in the composer.json file, check out the composer.json Schema Documentation.

To install the demo’s dependencies, continue as follows from the demo script’s directory:

composer install

The screenshot below shows a successful repository clone and dependency installation. Note that this screenshot shows cloning the repository using SSH; please use HTTPS as outlined above, if you are following along with this article.

Successful clone and dependency installation

Running the script

Before executing the script itself, let’s take a quick look at Prism Central and see how our categories are configured. The screenshot below shows the default categories in a Prism Central instance, along with one other category named CalmUsername, created by one of my colleagues during a recent live stream.

Live stream you say? What live stream? Check out NutanixDevLIVE, live on Twitch.tv every second Wednesday at 1500 PDT!

– Me, lol
Default Prism Central categories, plus one additional category created during a demo

Now that we are familiar with the categories that come with Prism Central “out of the box”, let’s run our demo script and create a few more, but using our PHP demo.

php categories.php

That happened quickly!

Running the demo script and creating Prism Central categories with PHP

And now, if we look at Prism Central again, we’ll see the categories specified in our JSON file have been created, as expected.

How does this work?

With the “demo” part of the demo out of the way, let’s look a little deeper into how this works. First, here is how our JSON spec is formatted.

{
	"categories": [
		{
			"keys": [
				"OSName",
				"OSVersion",
				"Status"
			],
			"values": [
				{
					"key": "OSName",
					"value": "Windows",
					"description": "VMs running a Windows OS variant"
				},
				{
					"key": "OSName",
					"value": "Linux",
					"description": "VMs running an Linux OS distribution"
				},
				{
					"key": "OSVersion",
					"value": "2012",
					"description": "VMs running Windows 2012"
				},
				{
					"key": "OSVersion",
					"value": "CentOS 8",
					"description": "VMs running CentOS Linux 8"
				},
				{
					"key": "Status",
					"value": "Dev",
					"description": "VMs running in the development environment"
				},
				{
					"key": "Status",
					"value": "Production",
					"description": "VMs running in the production environment"
				}
			]
		}
	]
}

By keeping our categories definitions (keys and values) in a separate JSON file, there’s no need to edit the script itself to change what categories get created. You could look at this as good development practice, depending on your opinion re such things. That said, the need to edit an app’s code to change things like this is something I try to avoid.

Next, the following block is the opening part of our demo script. It should be noted that this demo script is meant for that purpose only – demos. It shouldn’t be used in production without the understanding that modifications will be required to make it suitable for use in that type of environment.

Here we are bringing in the dependencies that were installed using PHP Composer. These are managed via autoload.php. We are also loading the environment configuration from the .env file outlined earlier. We’ve grabbed the Prism Central IP address, connection port and credentials ready for use in upcoming steps.

require_once('./vendor/autoload.php');

# load the script configuration
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();

$pc_ip = $_ENV['PC_IP'];
$pc_port = $_ENV['PC_PORT'];
$pc_username = $_ENV['PC_USERNAME'];
$pc_password = $_ENV['PC_PASSWORD'];

This block creates our API connection instance, using the GuzzleHttp package, then loads the categories definitions from the categories.json file. With that information available, we can begin building our Nutanix Prism Central REST API v3 BATCH requests.

# reusable Guzzle HTTP client instance
$client = new GuzzleHttp\Client();

# grab and decode the category details from the included JSON file
$raw_json = file_get_contents('categories.json');
$json = json_decode($raw_json);

Because we’re working with the BATCH API, we will first build the “base” of the request body. This is essentially an empty POST body that, while it will function on its own quite happily, won’t actually do anything other than return an HTTP 200 (assuming the connection was successful).

For more information on BATCH requests with the Nutanix Prism Central REST API, please see the article titled “Batch Brewing – Multiple Requests with the Nutanix APIs”, available below:

https://www.nutanix.dev/2019/11/19/batch-brewing-multiple-requests-with-the-nutanix-apis/

In this example, we are specifying that the BATCH request should continue processing the other requests, even if a request should fail. This is fine for this demo, but if a request carried out an action that must complete before the next request, “CONTINUE” above would need to be changed to “ABORT”. This means any failed request within the overall BATCH request would stop the remaining requests from being executed.

# start building the BATCH request
echo("Building the BATCH request payload that will create the category keys ...\n");
$batch_payload = [
	"action_on_failure" => "CONTINUE",
	"execution_order" => "SEQUENTIAL",
	"api_request_list" => [
	],
	"api_version" => "3.1"
];

With this base request available, we can plug our categories definitions into it. This is done by iterating over the list of category definitions in categories.json (category keys first), and adding them to the POST body.

foreach($json->categories[0]->keys as $key) {
	# do something with the keys here
	# probably use the API to create the keys, as shown below

	$category_key_payload = [
		"operation" => "PUT",
		"path_and_params" => "/api/nutanix/v3/categories/" . $key,
		"body" => [
			"name" => $key,
			"description" => $key,
			"capabilities" => [
				"cardinality" => 64
			],
			"api_version" => "3.1"
		]
	];

	# add the new API request to the batch payload's BODY
	$batch_payload["api_request_list"][] = $category_key_payload;
	
}

Each individual request within the main BATCH request is built as follows:

  • The request type is “PUT”
  • The request endpoint is https://[prism_central_ip_address]:[prism_central_port]/api/nutanix/v3/categories/[category_key]

As each request is built, it is added to the array containing all requests part of this parent BATCH request.

Lastly, with the entire BATCH API request built, we can submit all category key create requests in one go – this is the beauty of using the BATCH API for multiple requests.

Note: While this demo does contain the same type of request for all members of the parent BATCH request, this isn’t mandatory. A BATCH request could create a VM, upload an image or call any other API, all from one parent BATCH request.

The BATCH request is submitted to Prism Central using the convenient GuzzleHttp/Guzzle package installed earlier, with each request returning its own HTTP response as it is processed.

# submit the BATCH request that will create the category keys
echo("Creating the category keys via API BATCH request ...\n");
$res = $client->request('POST', 'https://' . $pc_ip . ':' . $pc_port . '/api/nutanix/v3/batch', [
	'auth' => [
		$pc_username,
		$pc_password
	],
	'verify' => false,
	'headers' => [
		'Content-Type' => 'application/json'
	],
	'body' => json_encode($batch_payload)
]);

The next section parses the response from our BATCH request, and displays information about each individual request contained within it i.e. HTTP response code and the request endpoint in this demo.

# check the results of the request
echo("BATCH request HTTP status code: " . $res->getStatusCode() . "\n");
$json_response = json_decode($res->getBody()->getContents());
echo("There are " . count($json_response->api_response_list) . " responses from this request.\n");
foreach($json_response as $response_item) {
	foreach($response_item as $response_details) {
		echo("Response code: " . $response_details->status . " | path_and_params: " . $response_details->path_and_params . "\n");
	}
}

The remaining parts of the script are essentially identical to what we’ve looked at so far, but repeat the process for the category values.

Why is this important?

As mentioned earlier, many Nutanix products as well as Prism & Prism Central features and options can be controlled via categories. For example, Nutanix Flow can be configured so that only VMs belonging to a specific category can talk to VMs that belong to the same category. Likewise, Nutanix Calm can deploy VMs using blueprints that specify which categories a VM should belong to, once it is deployed.

Even though this article has gone into a fair amount of detail on how to programatically create categories specifically using PHP, the DevOps “philosophy” makes clear the idea that automating processes and “flattening infrastructure” can simplify and streamline both operational and infrastructure processes. Being able to control this via script or code can further improve this process even more.

I hope this article was useful – thanks for reading and have a great day! 🙂