Ajouter un commentaire

1. Deploying a Debian GNU / Linux instance under 7 CloudStack


code update: 08/20/2013
- Improved code reading and better error handling.
- Fewer parameters to enter.
- Improved scalability for the following items.


...............................................................................................................
Learn how to deploy a GNU / Linux Debian 7 instance under CloudStack.

First steps, the user must:

  • Have a network already deployed (for accounts CloudStack Instances, it should already be the case).
  • Have the opportunity to create a new instance (not being at the creation limit).
  • Determine the network ID to which the instance will be connected(via the web interface or API, indications within the script).
  • Determine the ID of the service offering (configuration CPU / RAM / Network) required, indications within the script.

API Deployment

<?php
	// Complete list of API calls : http://cloudstack.apache.org/docs/api/apidocs-4.2/TOC_User.html

	##########################
	# API General parameters #
	##########################
	
	// Please, type your API/SECRET key below
	// We advise you to store this information into another included file
	define("APIKEY","<YOUR API KEY>");
	define("SECRETKEY","<YOUR SECRET KEY>");
	//Set the API's Endpoint
	define("ENDPOINT","https://cloudstack.ikoula.com/client/api");
	
	#########################################################
	# User parameters for instance deployment configuration #
	#########################################################
	
	//First instance:
	
		/* UUID of the network your instance will be connected to.
		Use API request 'listNetworks' or the WebUI to list existing networks and get the wanted network
		*/
		$vm01['conf']['networkid'] = "<ID of the network you want to use'>";
		
		/* UUID of the compute offering you want to use.
		Use API request 'listServiceOfferings' to list existing compute offerings.
		At the time being:
			- m1.small : c6b89fea-1242-4f54-b15e-9d8ec8a0b7e8
			- m1.medium : 8dae8be9-5dae-4f81-89d1-b171f25ef3fd
			- m1.large : d2b2e7b9-4ffa-419e-9ef1-6d413f08deab
			- m1.extralarge : 1412009f-0e89-4cfc-a681-1cda0631094b
		*/
		$vm01['conf']['serviceofferingid'] = "<UUID of the compute offering to use>";

		
		/* UUID of the OS template to use
		You can also use API request: 'listTemplates' to list existing templates.
		In this example script we are going to use the template named 'Debian 7 - Minimal - 64bits'
		*/
		$vm01['conf']['templateid'] = "06239517-eb64-441a-ae71-7a0c6d3a057a";
		
		/* Name of your choice for the instance (hostname and UI's display name)
		Alphanumerical characters only. This name mush be unique within your network. */
		$vm01['conf']['hostname'] = "<NAME OF YOUR INSTANCE>";
		
/* ------------------------------ DO NOT CHANGE ANYTHING PAST THIS LINE ! ----------------------------------- */
	$json_response = null;

	#######################################
	# Get zoneid for the provided network #
	#######################################
	// API Request
	$args['command'] = "listNetworks";

	// We're using the networkid provided earlier
	$args['id'] = $vm01['conf']['networkid'];

	// API request execution
	sendRequest($args, $json_response);

	// We store the zoneid for this networkid
	$vm01['conf']['zoneid'] = $json_response['listnetworksresponse']["network"][0]['zoneid'];

	// Reset vars used for this api call
	unset($args);
	
	############################################
	# Get public IP Address for this networkid #
	############################################
	// API request
	$args['command'] = "listPublicIpAddresses";
	$args['associatednetworkid'] = $vm01['conf']['networkid'];

	// API request execution
	sendRequest($args, $json_response);

	// We store the public IP id for this networkid
	$vm01['conf']['publicIPid'] = $json_response['listpublicipaddressesresponse']["publicipaddress"][0]['id'];

	// Reset vars used for this api call
	unset($args);
		
	###########################
	# First instance creation #
	###########################
	
	// API request
	$args['command'] = "deployVirtualMachine";
	$args['zoneid'] = $vm01['conf']['zoneid'];
	$args['serviceofferingid'] = $vm01['conf']['serviceofferingid'];
	$args['templateid'] = $vm01['conf']['templateid'];
	$args['networkids'] = $vm01['conf']['networkid'];
	$args['name'] = $vm01['conf']['hostname']; //Hostname
	$args['displayname'] = $args['name']; //Display name
	
	//Output type : JSON ou XML (XML by default)
	$args['response'] = "json";

	// Reset vars used before this api call
	
	// Init API's client
	sendRequest($args, $json_response);
	
	//We check for a job
	if(preg_match("/^[0-9a-f\-]+$/", $json_response['deployvirtualmachineresponse']['jobid']) > 0)
	{
		$jobs[] = $json_response['deployvirtualmachineresponse']['jobid'];
	}
	else{
		echo "Job ID not found.\n";
	}
	
	//We store the corresponding name/id of the instance within an array
	$vm01['conf']['id'] = $json_response['deployvirtualmachineresponse']['id'];
	
	//Async jobs check
	if(!checkJobs($jobs))
		exit;
	
/*-----------------------------------------------------------------------------------------------------------*/
	
	#############
	# Functions #
	#############
	
	//API's error management function
	function apiErrorCheck($json_response)
	{
		if(is_array($json_response))
		{
			$key = array_keys($json_response);
			if(isset($json_response['errorcode']))
			{
				echo "ERROR: ".$json_response['errorcode']." - ".$json_response['errortext']."\n";
				exit;
			}
			if(isset($json_response['errorcode']) || (isset($key[0]) && isset($json_response[$key[0]]['errorcode'])))
			{
				echo "ERROR: ".$json_response[$key[0]]['errorcode']." - ".$json_response[$key[0]]['errortext']."\n";
				exit;
			}
		}
		else
		{
			echo "ERROR: INVALID PARAMETER";
				exit;
		}
	}
	
	//Function to send requests to the API
	function sendRequest($args, &$json_response)
	{
		$json_response = null;
		// API key
		$args['apikey'] = APIKEY;
		$args['response'] = "json";
		// Sort parameters
		ksort($args);
		// We build the HTTP request based on arguments contained in $args
		$query = http_build_query($args);
		// Replace ' ' by '%20'
		$query = str_replace("+", "%20", $query);
		// We use the secret key and a HMAC SHA-1 algorithm on the request to encode the signature
		$hash = hash_hmac("SHA1",  strtolower($query), SECRETKEY, true);
		$base64encoded = base64_encode($hash);
		$signature = urlencode($base64encoded);

		// Finale request build based on the URL API + API request + parameters + Signature'
		$query .= "&signature=" . $signature;

		// $jobs = null;
		
		//Init API's client
		try
		{
			//Request building
			$httpRequest = new HttpRequest();
			$httpRequest->setMethod(HTTP_METH_POST);
			$httpRequest->setUrl(ENDPOINT . "?" . $query);
			
			// Send the request to the server
			$httpRequest->send();
			// Get API's return
			$response = $httpRequest->getResponseData();
			// Decoding API's response'
			$json_response = json_decode($response['body'], true);
			
			apiErrorCheck($json_response);
		}
		catch (Exception $e)
		{
			echo "Issue when sending request. ERROR=".$e->getMessage();
			exit;
		}
	}
	
	//Async jobs check function
	function checkJobs($jobs)
	{
		$json_response = null;
		$error_msg = "";
		if(is_array($jobs) && count($jobs) > 0)
		{
			// The task is an async one
			$secu = 0;
			// We list jobs
			$ij = 0;
			// As long as there is unfinished async jobs in verification stack, loop and check status
			while(count($jobs) > 0 && $secu < 100)
			{
				try
				{
					//We get the async job status
					$args['apikey'] = APIKEY;
					$args['command'] = "queryAsyncJobResult";
					$args['jobid'] = $jobs[$ij];
					$args['response'] = "json";
					
					$json_response = null;
					sendRequest($args, $json_response);
					
					if(is_array($json_response['queryasyncjobresultresponse']))
					{
						// if OK...
						if($json_response['queryasyncjobresultresponse']['jobstatus'] == 1)
						{
							// ...we remove the task from from verification stack
							//return("JOB OK\n");
							array_splice($jobs, $ij, 1);
						}
						// else...
						elseif($json_response['queryasyncjobresultresponse']['jobstatus'] == 2)
						{
							//...we store the error then remove the stack from the verification stack
							//return("JOB ERROR\n");
							array_splice($jobs, $ij, 1);
							$error_msg .= "ERROR ! RESULT_CODE=".$json_response['queryasyncjobresultresponse']['jobresultcode'];
						}
						// That task is still ongoing, we move on to the next one
						elseif($json_response['queryasyncjobresultresponse']['jobstatus'] == 0)
						{
							// Next task
							$ij++;
							// Go easy on the API
							sleep(5);
						}
					}
				}
				catch(Exception $e)
				{
					$error_msg .= "EXCEPTION during async job task's check''''. JOB_UUID:".$jobs[$ij]." ERROR=".$e->getMessage()." \n";
				}

				// If the index reach the end of the array, we reset it.
				if($ij == count($jobs))
				{
					$ij = 0;
					$secu++;
				}
			}

			if($error_msg)
			{
				echo "ERRORS:".$error_msg."\n";
				return false;
			}
			return true;
		}
		echo "No job\n";
		return false;
	}
?>

The main interest of the operations via the API is the timeliness and process flexibility.

Some advantages of the CloudStack interface are its ergonomy and at the scope of the majority but the API goes further and allows rapid and automated deployments of one or more ready-made instances.

This script will allow you to programmatically deploy a Debian "Wheezy" body ready. The version here is very simplified example but it is quite possible through minor modifications to make the creation of instances of other "hard" configurations (Memory / CPU / Network changing supply of service) or software (via the modification of the model chosen for another operating system).

Similarly, the programmatic aspect allows the creation of multiple instances without user intervention (by making automatically change the name for each instance).

Through the execution repetition it's possible to deploy complete clusters of identical instances in a very short time.

In addition, the above example is based on the PHP5 language but it is quite possible to use other programming languages ​​according to your preferences or restrictions.

More examples to come ...