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é 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
systeme
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
presence
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
occurences
de
'+'
par
des
'%20'
$
query
=
str_replace("
+
"
,
"
%
20
"
,
$
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
a
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
verification
la
tache
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▲
Merci à Wiztricks pour sa relecture technique.
Merci à Ikoula pour ce tutoriel.