I. Introduction▲
Dans un premier temps, l'utilisateur doit :
- disposer d'un réseau déjà déployé (pour les comptes CloudStack Instances c'est normalement le cas) ;
- avoir la possibilité de créer une nouvelle instance (ne pas être à sa limite de création donc) ;
- déterminer l'ID du réseau auquel sera connectée son instance (via l'interface web ou l'API, de même, indications en commentaires dans le script) ;
- déterminer l'ID de l'offre de service (configuration CPU/RAM/Réseau) voulue, indications au sein du script.
II. II Déploiement de l'API▲
II-A. Script▲
<?php
// Liste complète des appels possibles : http://download.cloud.com/releases/3.0.6/api_3.0.6/TOC_User.html
################################
# Paramètres généraux de l'API #
################################
// Renseignez ici votre clef API ainsi que votre clef secrète
// Nous conseillons de déporter cette information au sein d'un autre fichier PHP dont on restreindra l'accès
define("APIKEY"
,
"d1Ii4ON33yqUahgtlsjnzK0o7QXNzXZONZKcW_rDcOV4jln48b5ujsSvU-mo9KsoLbp631VyCI4fLb3u176JZg"
);
define("SECRETKEY"
,
"fBenapO2hjOd3Vaj-NDmrekTIK85B8Jl_m8wFld4gi0XvLXy4w0iJz-2srztoVqkGVoblT25DyG4709gibo_TA"
);
//On définit l'URL d'appel de l'API (ou 'EndPoint')
define("ENDPOINT"
,
"https://cloudstack.ikoula.com/client/api"
);
#############################################################
# Paramètres utilisateur de configuration d(es) instance(s) #
#############################################################
//Première instance :
/* UUID(s) du réseau auquel sera connectée votre instance.
Utilisez la requête API 'listNetworks'
ou l'interface pour lister les réseaux existants et déterminer le réseau voulu
*/
$vm01
[
'conf'
][
'networkid'
]
=
"<ID DU RESEAU QUE VOUS SOUHAITEZ UTILISER>"
;
/* UUID de l'offre de service (configuration matérielle) voulue.
Utilisez la requête API 'listServiceOfferings' pour lister les offres existantes.
Actuellement :
- 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'
]
=
"<ID DE L'OFFRE DE SERVICE A UTILISER>"
;
/* UUID du modèle de système d'exploitation.
Vous pouvez aussi utiliser la requête API 'listTemplates' pour lister les modèles existants.
Dans notre exemple nous allons nous baser sur le template 'Debian 7 - Minimal - 64bits'
*/
$vm01
[
'conf'
][
'templateid'
]
=
"6d5b069b-4268-4c7c-810f-2793c2ac44fb"
;
/* Nom de votre choix pour l'instance (Nom de la machine et d'affichage dans l'interface)
Caractères alphanumériques uniquement. Ce nom doit être unique au sein de votre réseau. */
$vm01
[
'conf'
][
'hostname'
]
=
"<NOM DE VOTRE INSTANCE>"
;
/* ------------------ NE PLUS RIEN MODIFIER APRES CETTE LIGNE ---------------------- */
$json_response
=
null
;
################################################
# Récupération de l'ID de zone pour ce network #
################################################
// Requête API
$args
[
'command'
]
=
"listNetworks"
;
// On reprend l'ID de réseau renseigné plus tôt
$args
[
'id'
]
=
$vm01
[
'conf'
][
'networkid'
];
// Execution de la requête
sendRequest($args
,
$json_response
);
// Stockage de l'ID de zone pour ce réseau
$vm01
[
'conf'
][
'zoneid'
]
=
$json_response
[
'listnetworksresponse'
][
"network"
][
0
][
'zoneid'
];
// On réinitialise les variables utilisées pour l'envoi de requêtes API
unset($args
);
######################################################
# Récupération de l'id de l'adresse IP de ce network #
######################################################
// Requête API
$args
[
'command'
]
=
"listPublicIpAddresses"
;
$args
[
'associatednetworkid'
]
=
$vm01
[
'conf'
][
'networkid'
];
// Execution de la requête
sendRequest($args
,
$json_response
);
// Stockage de l'ID de l'adresse IP publique du réseau
$vm01
[
'conf'
][
'publicIPid'
]
=
$json_response
[
'listpublicipaddressesresponse'
][
"publicipaddress"
][
0
][
'id'
];
// On réinitialise les variables utilisées pour l'envoi de requêtes API
unset($args
);
####################################
# Création de la première instance #
####################################
// Requête API
$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'
];
//Nom d'affichage
//Type de retour : JSON ou XML (par défaut : XML)
$args
[
'response'
]
=
"json"
;
//On réinitialise les variables avant l'appel API
// Initialisation du client API
sendRequest($args
,
$json_response
);
//On vérifie la présence d'un Job
if
(preg_match("/^[0-9a-f
\-
]+$/"
,
$json_response
[
'deployvirtualmachineresponse'
][
'jobid'
]
) >
0
)
{
$jobs
[]
=
$json_response
[
'deployvirtualmachineresponse'
][
'jobid'
];
}
else
{
echo "ID de job non trouvé.
\n
"
;
}
//On mémorise la correspondance name/id de l'instance au sein d'un tableau
$vm01
[
'conf'
][
'id'
]
=
$json_response
[
'deployvirtualmachineresponse'
][
'id'
];
//On utilise la fonction de vérification des jobs asynchrones
if
(!
checkJobs($jobs
))
exit;
/*-----------------------------------------------------------------------------------------------------------*/
#############
# Fonctions #
#############
//Fonction de gestion d'erreur(s) API
function
apiErrorCheck($json_response
)
{
if
(is_array($json_response
))
{
$key
=
array_keys($json_response
);
if
(isset($json_response
[
'errorcode'
]
))
{
echo "ERREUR : "
.
$json_response
[
'errorcode'
].
" - "
.
$json_response
[
'errortext'
].
"
\n
"
;
exit;
}
if
(isset($json_response
[
'errorcode'
]
) ||
(isset($key
[
0
]
) &&
isset($json_response
[
$key
[
0
]][
'errorcode'
]
)))
{
echo "ERREUR : "
.
$json_response
[
$key
[
0
]][
'errorcode'
].
" - "
.
$json_response
[
$key
[
0
]][
'errortext'
].
"
\n
"
;
exit;
}
}
else
{
echo "ERREUR : PARAMETRE INVALIDE"
;
exit;
}
}
//Fonction d'envoi de requête à l'API
function
sendRequest($args
,
&
$json_response
)
{
$json_response
=
null
;
// Clef API
$args
[
'apikey'
]
=
APIKEY;
$args
[
'response'
]
=
"json"
;
//On classe les paramètres
ksort($args
);
// On construit la requête HTTP basée sur les paramètres contenus dans $args
$query
=
http_build_query($args
);
// On s'assure de bien remplacer toutes les occurrences de '+' par des ' '
$query
=
str_replace("+"
,
" "
,
$query
);
//On utilise la clef secrète et un algorithme HMAC SHA-1 sur la requête pour encoder la signature
$hash
=
hash_hmac("SHA1"
,
strtolower($query
),
SECRETKEY,
true
);
$base64encoded
=
base64_encode($hash
);
$signature
=
urlencode($base64encoded
);
// Construction de la requête finale sous la forme 'URL API + Requête API et paramètres + Signature'
$query
.=
"&signature="
.
$signature
;
// $jobs = null;
// Initialisation du client API
try
{
//Construction de la requête
$httpRequest
=
new
HttpRequest();
$httpRequest
->
setMethod(HTTP_METH_POST);
$httpRequest
->
setUrl(ENDPOINT .
"?"
.
$query
);
// Envoi de la requête au serveur :
$httpRequest
->
send();
// Récupération du retour de l'API
$response
=
$httpRequest
->
getResponseData();
// retour de la réponse
$json_response
=
json_decode($response
[
'body'
],
true
);
apiErrorCheck($json_response
);
}
catch
(Exception
$e
)
{
echo "Probleme lors de l'envoi de la requête. ERREUR="
.
$e
->
getMessage();
exit;
}
}
//Fonction de vérification des jobs asynchrones
function
checkJobs($jobs
)
{
$json_response
=
null
;
$error_msg
=
""
;
if
(is_array($jobs
) &&
count($jobs
) >
0
)
{
// La tâche est asynchrone, on doit donc régulièrement vérifier les tâches avec une sécurité
$secu
=
0
;
// On indexe les tâches
$ij
=
0
;
// Tant qu'il y a des tâches asynchrones non terminées dans la pile de vérification, on boucle et on vérifie le statut
while
(count($jobs
) >
0
&&
$secu
<
100
)
{
try
{
//On interroge le statut de la tâche asynchrone
// http://download.cloud.com/releases/3.0.6/api_3.0.6/root_admin/queryAsyncJobResult.html
$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'
]
))
{
// Si OK...
if
($json_response
[
'queryasyncjobresultresponse'
][
'jobstatus'
]
==
1
)
{
// ...On retire simplement la tâche du tableau à surveiller
//return("JOB OK\n");
array_splice($jobs
,
$ij
,
1
);
}
// Sinon...
elseif
($json_response
[
'queryasyncjobresultresponse'
][
'jobstatus'
]
==
2
)
{
//...On mémorise l'erreur et on retire la tâche du tableau à surveiller
//return("JOB ERREUR\n");
array_splice($jobs
,
$ij
,
1
);
$error_msg
.=
"ERREUR ! RESULT_CODE="
.
$json_response
[
'queryasyncjobresultresponse'
][
'jobresultcode'
];
}
// Cette tâche est encore en cours, on passe à la suivante et on temporise
elseif
($json_response
[
'queryasyncjobresultresponse'
][
'jobstatus'
]
==
0
)
{
// Tâche suivante
$ij
++;
// Temporisation entre chaque interrogation pour ne pas charger inutilement l'API
sleep(5
);
}
}
}
catch
(Exception
$e
)
{
$error_msg
.=
"EXCEPTION Lors de la vérification la tâche asynchrone. JOB_UUID:"
.
$jobs
[
$ij
].
" ERREUR="
.
$e
->
getMessage().
"
\n
"
;
}
// Si l'index arrive en bout de tableau, on le réinitialise
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
;
}
?>
II-B. Conclusion▲
Le principal intérêt des opérations via l'API est la rapidité d'exécution et la flexibilité des procédés.
L'interface CloudStack a comme avantages d'être ergonomique et à la portée de la majorité, mais l'API va encore plus loin et permet des déploiements rapides et automatisés d'une ou plusieurs instances prêtes à l'emploi.
Ce script vous permettra donc de déployer de manière programmatique une instance Debian « Wheezy » prête à l'emploi. La version fournie ici en exemple est très simplifiée, mais il est tout à fait possible via quelques modifications mineures de procéder à la création d'instances d'autres configurations « matérielles » (Mémoire/Processeur/Réseau en changeant d'offre de service) ou logicielles (via la modification du modèle choisi pour un autre système d'exploitation).
De même, cet aspect programmatique permet la création multiple d'instances sans intervention de l'utilisateur (en faisant en sorte de changer automatiquement le nom pour chaque instance).
Via la répétition de son exécution, on peut donc imaginer déployer des clusters complets d'instances identiques en un temps très réduit.
De plus, l'exemple ci-dessus est fourni sur la base du langage PHP5, mais il est tout à fait possible d'utiliser d'autres langages de programmation selon vos préférences ou contraintes.
III. Remerciements Developpez▲
L'équipe Cloud Computing tient à remercier Ikoula pour la rédaction de ce tutoriel.
Nos remerciements à Wiztricks pour sa relecture technique et Zoom61 pour sa gabarisation.
N'hésitez pas à commenter cet article ! Commentez