Version : 2022.01

Dernière mise-à-jour : 2022/06/12 14:07

DOF403 - Variables, Expressions, Facts et Itérations

Contenu du Module

  • DOF403 - Variables, Expressions, Facts et Itérations
    • Contenu du Module
    • LAB #1 - Variables
      • Variables Simples
      • Tableaux
      • Hashes
    • LAB #2 - Expressions
      • Expressions Mathématiques
      • Expression Booléennes
      • Expressions Régulières
      • Expressions Conditionnelles
    • LAB #3 - Facts
      • Facts dans un Hash
      • Facts dans une Expression
      • Facts Externes
      • Facts Exécutables
    • LAB #4 - Itérations
      • Itération et Tableaux
      • Itération et Hashes

LAB #1 - Variables

Variables Simples

Une variable sous Puppet est une façon de donner un nom à une valeur. Par exemple :

$php_package = 'php7.0-cli'

package { $php_package:
  ensure => installed,
}

Important - Le nom d'une variable doit commencer par le caractère $ puis par une lettre minuscule ou un underscore.

Le contenu de la variable peut être :

  • une chaîne,
  • un chiffre,
  • un booléen.
$my_name = 'Zaphod Beeblebrox'
$answer = 42
$scheduled_for_demolition = true

Important - La valeur d'un variable booléenne doit être true ou false.

Les variables peuvent ensuite être appelées ainsi :

vagrant@ubuntu-xenial:~$ sudo vi string_interpolation.pp
vagrant@ubuntu-xenial:~$ cat string_interpolation.pp
$my_name = 'John'
notice("Hello, ${my_name}! It's great to meet you!")

vagrant@ubuntu-xenial:~$ sudo puppet apply string_interpolation.pp 
Notice: Scope(Class[main]): Hello, John! It's great to meet you!
Notice: Compiled catalog for ubuntu-xenial in environment production in 0.01 seconds
Notice: Applied catalog in 0.01 seconds

Important - Notez l'utilisation de l'attribut notice qui imprime la chaîne et le contenu de la variable à l'écran.

Tableaux

Un tableau est une groupe de valeurs. Par exemple :

$heights = [193, 120, 181, 164, 172]

$first_height = $heights[0]

Il est possible de référencer une des valeurs en utilisant son index. Par exemple $heights[0]=193 et $heights[4]=172.

Dans l'exemple de l'utilisation d'un tableaux ci-dessous, le tableau contient une liste de dépendances qui doivent être installées sur le système :

vagrant@ubuntu-xenial:~$ sudo vi resource_array.pp
vagrant@ubuntu-xenial:~$ cat resource_array.pp
$dependencies = [
  'php7.0-cgi',
  'php7.0-cli',
  'php7.0-common',
  'php7.0-gd',
  'php7.0-json',
  'php7.0-mcrypt',
  'php7.0-mysql',
  'php7.0-soap',
]

package { $dependencies:
  ensure => installed,
}

Appliquez ce manifest :

vagrant@ubuntu-xenial:~$ sudo apt-get update
Hit:1 http://archive.ubuntu.com/ubuntu xenial InRelease
Get:2 http://archive.ubuntu.com/ubuntu xenial-updates InRelease [109 kB]                                                   
Get:3 http://security.ubuntu.com/ubuntu xenial-security InRelease [109 kB]                                                   
Get:4 http://archive.ubuntu.com/ubuntu xenial-backports InRelease [107 kB]                              
Hit:5 http://apt.puppetlabs.com xenial InRelease                                                                   
Fetched 325 kB in 0s (472 kB/s)
Reading package lists... Done

vagrant@ubuntu-xenial:~$ sudo puppet apply resource_array.pp
Notice: Compiled catalog for ubuntu-xenial in environment production in 0.48 seconds
Notice: /Stage[main]/Main/Package[php7.0-cgi]/ensure: created
Notice: /Stage[main]/Main/Package[php7.0-gd]/ensure: created
Notice: /Stage[main]/Main/Package[php7.0-mcrypt]/ensure: created
Notice: /Stage[main]/Main/Package[php7.0-mysql]/ensure: created
Notice: /Stage[main]/Main/Package[php7.0-soap]/ensure: created
Notice: Applied catalog in 16.33 seconds

Comme vous pouvez constater, Puppet a créé une ressource pour chaque paquet qui doit être installé :

Notice: /Stage[main]/Main/Package[php7.0-cgi]/ensure: created
Notice: /Stage[main]/Main/Package[php7.0-gd]/ensure: created
Notice: /Stage[main]/Main/Package[php7.0-mcrypt]/ensure: created
Notice: /Stage[main]/Main/Package[php7.0-mysql]/ensure: created
Notice: /Stage[main]/Main/Package[php7.0-soap]/ensure: created

Hashes

Un Hash est comme un Tableau, à l'exception du fait que chaque élément a un nom appelé une clef :

vagrant@ubuntu-xenial:~$ sudo vi variable_hash.pp
vagrant@ubuntu-xenial:~$ cat variable_hash.pp
$heights = {
  'john'    => 193,
  'rabiah'  => 120,
  'abigail' => 181,
  'melina'  => 164,
  'sumiko'  => 172,
}

notice("John's height is ${heights['john']}cm.")

Appliquez ce manifest :

vagrant@ubuntu-xenial:~$ sudo puppet apply variable_hash.pp
Notice: Scope(Class[main]): John's height is 193cm.
Notice: Compiled catalog for ubuntu-xenial in environment production in 0.01 seconds
Notice: Applied catalog in 0.01 seconds

Un Hash peut être utilisé pour définir les attributs d'une ressource :

vagrant@ubuntu-xenial:~$ sudo vi hash_attributes.pp
vagrant@ubuntu-xenial:~$ cat hash_attributes.pp
$attributes = {
  'owner' => 'ubuntu',
  'group' => 'ubuntu',
  'mode'  => '0644',
}

file { '/tmp/test':
  ensure => present,
  *      => $attributes,
}

Important - Le caractère * informe Puppet d'utiliser ce Hash comme une liste d'attributs.

Appliquez ce manifest :

vagrant@ubuntu-xenial:~$ sudo puppet apply hash_attributes.pp
Notice: Compiled catalog for ubuntu-xenial in environment production in 0.01 seconds
Notice: /Stage[main]/Main/File[/tmp/test]/ensure: created
Notice: Applied catalog in 0.01 seconds

vagrant@ubuntu-xenial:~$ ls -l /tmp/test
-rw-r--r-- 1 ubuntu ubuntu 0 Feb 11 13:42 /tmp/test

LAB #2 - Expressions

Expressions Mathématiques

Puppet peut gérer des expressions mathématiques. Créez donc le fichier expression_numeric.pp :

vagrant@ubuntu-xenial:~$ sudo vi expression_numeric.pp
vagrant@ubuntu-xenial:~$ cat expression_numeric.pp
$value = (17 * 8) + (12 / 4) - 1
notice($value)

L'application de ce manifest affichera le résultat de l'expression :

vagrant@ubuntu-xenial:~$ sudo puppet apply expression_numeric.pp 
Notice: Scope(Class[main]): 138
Notice: Compiled catalog for ubuntu-xenial in environment production in 0.01 seconds
Notice: Applied catalog in 0.01 seconds

Expression Booléennes

Puppet sait aussi gérer des expression booléennes, c'est-à-dire des expressions qui produisent comme résultat soit vrai soit faux. Créez donc le fichier expression_boolean.pp :

vagrant@ubuntu-xenial:~$ sudo vi expression_boolean.pp
vagrant@ubuntu-xenial:~$ cat expression_boolean.pp
notice(9 < 10)
notice(11 > 10)
notice(10 >= 10)
notice(10 <= 10)
notice('foo' == 'foo')
notice('foo' in 'foobar')
notice('foo' in ['foo', 'bar'])
notice('foo' in { 'foo' => 'bar' })
notice('foo' =~ /oo/)
notice('foo' =~ String)
notice(1 != 2)

Les opérateurs utilisés dans le fichier expression_boolean.pp sont les suivants :

Opérateur Description
== Égal
!= Pas égal
>, >=, < et <= Supérieur, supérieur ou égal, inférieur, inférieur ou égal
A in B A est une sous-chaîne de B, A est un élément du tableau B ou A est une clef du Hash B
A =~ B A correspond à l'expression régulière B ou A est une valeur qui correspond au type de données de B ( p.e. 'trainee' =~ String est vrai )
Valeur =~ ER Si valeur égal l'ER alors vrai
/a+/ Correspond à a aa aaa etc

Les expressions dans le fichier expression_boolean.pp donnent toutes un résultat de vrai, comme vous pouvez voir en appliquant le manifest :

vagrant@ubuntu-xenial:~$ sudo puppet apply expression_boolean.pp 
Notice: Scope(Class[main]): true
Notice: Scope(Class[main]): true
Notice: Scope(Class[main]): true
Notice: Scope(Class[main]): true
Notice: Scope(Class[main]): true
Notice: Scope(Class[main]): true
Notice: Scope(Class[main]): true
Notice: Scope(Class[main]): true
Notice: Scope(Class[main]): true
Notice: Scope(Class[main]): true
Notice: Scope(Class[main]): true
Notice: Compiled catalog for ubuntu-xenial in environment production in 0.01 seconds
Notice: Applied catalog in 0.01 seconds

Expressions Régulières

Les expression régulières dans Puppet doivent être entourées du caractère /. Créez don le fichier regex.pp :

vagrant@ubuntu-xenial:~$ sudo vi regex.pp
vagrant@ubuntu-xenial:~$ cat regex.pp
$candidate = 'foo'
notice($candidate =~ /foo/) # literal
notice($candidate =~ /f/)   # substring
notice($candidate =~ /f.*/) # f followed by zero or more characters
notice($candidate =~ /f.o/) # f, any character, o
notice($candidate =~ /fo+/) # f followed by one or more 'o's
notice($candidate =~ /[fgh]oo/) # f, g, or h followed by 'oo'

Comme la valeur contenue dans $candidate correspond à chaque expression régulière dans le manifest, le résultat est toujours vrai :

vagrant@ubuntu-xenial:~$ sudo puppet apply regex.pp
Notice: Scope(Class[main]): true
Notice: Scope(Class[main]): true
Notice: Scope(Class[main]): true
Notice: Scope(Class[main]): true
Notice: Scope(Class[main]): true
Notice: Scope(Class[main]): true
Notice: Compiled catalog for ubuntu-xenial in environment production in 0.01 seconds
Notice: Applied catalog in 0.01 seconds

Important - La syntaxe des expressions régulières Puppet est la même que celle de Ruby. Vous pouvez trouver plus d'informations concernant cette syntaxe à l'adresse suivante : http://ruby-doc.org/core/Regexp.html.

Expressions Conditionnelles

Puppet peut utiliser des expressions. Créez le fichier if.pp :

vagrant@ubuntu-xenial:~$ sudo vi if.pp
vagrant@ubuntu-xenial:~$ cat if.pp
$install_perl = true
if $install_perl {
  package { 'perl':
    ensure => installed,
  }
} else {
  package { 'perl':
    ensure => absent,
  }
}

Appliquez ce menifest :

vagrant@ubuntu-xenial:~$ sudo puppet apply if.pp
Notice: Compiled catalog for ubuntu-xenial in environment production in 0.55 seconds
Notice: Applied catalog in 0.04 seconds

Contrôlez maintenant si la commande dpkg –get-selections retourne une valeur d'install pour le paquet perl :

vagrant@ubuntu-xenial:~$ dpkg --get-selections | grep perl
libapparmor-perl				install
liberror-perl					install
liblocale-gettext-perl				install
libperl5.22:amd64				install
libtext-charwidth-perl				install
libtext-iconv-perl				install
libtext-wrapi18n-perl				install
perl						install
perl-base					install
perl-modules-5.22				install

Modifiez maintenant la valeur de $install_perl dans le fichier if.pp :

vagrant@ubuntu-xenial:~$ sudo vi if.pp
vagrant@ubuntu-xenial:~$ cat if.pp
$install_perl = false
if $install_perl {
  package { 'perl':
    ensure => installed,
  }
} else {
  package { 'perl':
    ensure => absent,
  }
}

Appliquez maintenant le manifest modifié :

vagrant@ubuntu-xenial:~$ sudo puppet apply if.pp
Notice: Compiled catalog for ubuntu-xenial in environment production in 0.46 seconds
Notice: /Stage[main]/Main/Package[perl]/ensure: removed
Notice: Applied catalog in 2.03 seconds

Contrôlez maintenant si la commande dpkg –get-selections retourne une valeur d'deinstall pour le paquet perl :

vagrant@ubuntu-xenial:~$ dpkg --get-selections | grep perl
libapparmor-perl				install
liblocale-gettext-perl				install
libperl5.22:amd64				install
libtext-charwidth-perl				install
libtext-iconv-perl				install
libtext-wrapi18n-perl				install
perl						deinstall
perl-base					install
perl-modules-5.22				install

HERE

Une expression conditionnelle utilisant un if ne permet que deux choix. S'il faut plus de choix, il convient d'utiliser case. Créez donc le fichier case.pp :

vagrant@ubuntu-xenial:~$ sudo vi case.pp
vagrant@ubuntu-xenial:~$ cat case.pp
$webserver = 'nginx'
case $webserver {
  'nginx': {
    notice("Looks like you're using Nginx! Good choice!")
  }
  'apache': {
    notice("Ah, you're an Apache fan, eh?")
  }
  'IIS': {
    notice('Well, somebody has to.')
  }
  default: {
    notice("I'm not sure which webserver you're using!")
  }
}

En appliquant ce manifest, Puppet retourne la chaîne Looks like you're using Nginx! Good choice! :

vagrant@ubuntu-xenial:~$ sudo puppet apply case.pp
Notice: Scope(Class[main]): Looks like you're using Nginx! Good choice!
Notice: Compiled catalog for ubuntu-xenial in environment production in 0.01 seconds
Notice: Applied catalog in 0.01 seconds

LAB #3 - Facts

Les manifests de Puppet ont souvent besoin de connaître quelque chose concernant le système, par exemple :

  • Le nom d'hôte,
  • L'adresse IP,
  • La version du système d'exploitation.

Sous Puppet, le mécanisme qui permet d'obtenir ces informations s'appelle Facter et chaque information fournie par Facter s'appelle un Fact.

Facts dans un Hash

Créez donc le fichier facts_hash.pp :

vagrant@ubuntu-xenial:~$ sudo vi facts_hash.pp
vagrant@ubuntu-xenial:~$ cat facts_hash.pp
notice($facts['kernel'])

Important - Dans ce manifest est utilisé la variable $facts. Le fact recherché est fourni en tant que clef - dans notre exemple kernel.

En appliquant ce manifest, vous obtiendrez donc le type de noyau du système d'exploitation :

vagrant@ubuntu-xenial:~$ sudo puppet apply facts_hash.pp
Notice: Scope(Class[main]): Linux
Notice: Compiled catalog for ubuntu-xenial in environment production in 0.01 seconds
Notice: Applied catalog in 0.01 seconds

L'utilisation de la commande facter permet de se renseigner sur les Facts disponibles, par exemple ceux concernant le système d'exploitation :

vagrant@ubuntu-xenial:~$ facter os
{
  architecture => "amd64",
  distro => {
    codename => "xenial",
    description => "Ubuntu 16.04.6 LTS",
    id => "Ubuntu",
    release => {
      full => "16.04",
      major => "16.04"
    }
  },
  family => "Debian",
  hardware => "x86_64",
  name => "Ubuntu",
  release => {
    full => "16.04",
    major => "16.04"
  },
  selinux => {
    enabled => false
  }
}

Important - Comme vous pouvez constater, la sortie est sous forme de Hashs multiples.

Pour accéder à une valeur dans un Hash, il convient de spécifier la clef contenant la valeur entre les caractères [ ]. Pour spécifier une clef dans une clef, il convient de nouveau à la spécifier entre des caractères [ ]. Créez le fichier facts_architecture.pp :

vagrant@ubuntu-xenial:~$ sudo vi facts_architecture.pp
vagrant@ubuntu-xenial:~$ cat facts_architecture.pp
notice($facts['os']['architecture'])

Appliquez le manifest et vous obtenez la valeur d'architecture :

vagrant@ubuntu-xenial:~$ sudo puppet apply facts_architecture.pp
Notice: Scope(Class[main]): amd64
Notice: Compiled catalog for ubuntu-xenial in environment production in 0.01 seconds
Notice: Applied catalog in 0.01 seconds

Cette même technique est applicable quelque soit le profondeur du Hash. Pour obtenir la valeur du codename, créez le fichier facts_distro_codename.pp :

vagrant@ubuntu-xenial:~$ sudo vi facts_distro_codename.pp
vagrant@ubuntu-xenial:~$ cat facts_distro_codename.pp
notice($facts['os']['distro']['codename'])

Appliquez le manifest et vous obtenez la valeur xenial :

vagrant@ubuntu-xenial:~$ sudo puppet apply facts_distro_codename.pp
Notice: Scope(Class[main]): xenial
Notice: Compiled catalog for ubuntu-xenial in environment production in 0.01 seconds
Notice: Applied catalog in 0.01 seconds

Pour obtenir la valeur du numéro de version de la distribution, créez le fichier facts_major :

vagrant@ubuntu-xenial:~$ sudo vi facts_major.pp
vagrant@ubuntu-xenial:~$ cat facts_major.pp
notice($facts['os']['release']['major'])

Appliquez le manifest et vous obtenez la valeur 16.04 :

vagrant@ubuntu-xenial:~$ sudo puppet apply facts_major.pp
Notice: Scope(Class[main]): 16.04
Notice: Compiled catalog for ubuntu-xenial in environment production in 0.01 seconds
Notice: Applied catalog in 0.01 seconds

Pour obtenir le nom d'hôte, le FQDN et l'adresse IP de la machine, créez le fichier fact_networking.pp :

vagrant@ubuntu-xenial:~$ sudo vi fact_networking.pp
vagrant@ubuntu-xenial:~$ cat fact_networking.pp
notice("My hostname is ${facts['hostname']}")
notice("My FQDN is ${facts['fqdn']}")
notice("My IP is ${facts['networking']['ip']}")

Appliquez le manifest :

vagrant@ubuntu-xenial:~$ sudo puppet apply fact_networking.pp
Notice: Scope(Class[main]): My hostname is ubuntu-xenial
Notice: Scope(Class[main]): My FQDN is ubuntu-xenial
Notice: Scope(Class[main]): My IP is 10.0.2.15
Notice: Compiled catalog for ubuntu-xenial in environment production in 0.01 seconds
Notice: Applied catalog in 0.01 seconds

Facts dans une Expression

Il est aussi possible de référencer un Fact dans une expression. Créez don le fichier fact_ip.pp :

vagrant@ubuntu-xenial:~$ sudo vi fact_ip.pp
vagrant@ubuntu-xenial:~$ cat fact_ip.pp
if $facts['os']['selinux']['enabled'] {
  notice('SELinux is enabled')
} else {
  notice('SELinux is disabled')
}

Appliquez le manifest :

vagrant@ubuntu-xenial:~$ sudo puppet apply fact_ip.pp
Notice: Scope(Class[main]): SELinux is disabled
Notice: Compiled catalog for ubuntu-xenial in environment production in 0.01 seconds
Notice: Applied catalog in 0.01 seconds

Facts permettent la configuration dynamique de variables pour les applications. Dans le cas suivant, on va configurer la variable MariaDB innodb_buffer_pool_size. Créez donc le fichier fact_memory.pp :

vagrant@ubuntu-xenial:~$ sudo vi fact_memory.pp
vagrant@ubuntu-xenial:~$ cat fact_memory.pp
$buffer_pool = $facts['memory']['system']['total_bytes'] * 3/4
notice("innodb_buffer_pool_size=${buffer_pool}")

En appliquant ce manifest la valeur de la directive innodb_buffer_pool_size est fixée à 75% de la mémoire RAM totale :

vagrant@ubuntu-xenial:~$ sudo puppet apply fact_memory.pp
Notice: Scope(Class[main]): innodb_buffer_pool_size=780091392
Notice: Compiled catalog for ubuntu-xenial in environment production in 0.01 seconds
Notice: Applied catalog in 0.01 seconds

Facts Externes

Facts Externes sont des Facts créés par l'Administrateur. Puppet cherche des Facts Externes dans le répertoire /opt/puppetlabs/facter/facts.d/. Créez le fichier fact_external.txt et copiez-le dans le répertoire /opt/puppetlabs/facter/facts.d/ :

vagrant@ubuntu-xenial:~$ sudo vi fact_external.txt
vagrant@ubuntu-xenial:~$ cat fact_external.txt
cloud=aws
vagrant@ubuntu-xenial:~$ sudo cp fact_external.txt /opt/puppetlabs/facter/facts.d/

Utilisez ensuite la commande facter pour vérifier que le Fact fonctionne :

vagrant@ubuntu-xenial:~$ sudo facter cloud
aws

Dans un manifest, un Fact Externe est référencé exactement de la même façon qu'un Fact Interne. Créez le fichier fact_cloud.pp :

vagrant@ubuntu-xenial:~$ sudo vi fact_cloud.pp
vagrant@ubuntu-xenial:~$ cat fact_cloud.pp
case $facts['cloud'] {
  'aws': {
    notice('This is an AWS cloud server ')
  }
  'gcp': {
    notice('This is a Google cloud server')
  }
  default: {
    notice("I'm not sure which cloud I'm in!")
  }
}

Appliquez le manifest pour obtenir le résultat :

vagrant@ubuntu-xenial:~$ sudo puppet apply fact_cloud.pp 
Notice: Scope(Class[main]): This is an AWS cloud server 
Notice: Compiled catalog for ubuntu-xenial in environment production in 0.01 seconds
Notice: Applied catalog in 0.01 seconds

Facts Exécutables

Facts Externes ne sont pas uniquement des fichiers textes statiques. Ils peuvent aussi être la sortie d'un script ou d'une commande. Créez le script date.sh et copiez-le dans le répertoire /opt/puppetlabs/facter/facts.d/ :

vagrant@ubuntu-xenial:~$ sudo vi date.sh
vagrant@ubuntu-xenial:~$ cat date.sh
#!/bin/bash
echo "date=`date +%F`"
vagrant@ubuntu-xenial:~$ sudo cp date.sh /opt/puppetlabs/facter/facts.d/

Rendez le script exécutable et vérifiez avec la commande facter que le Fact fonctionne :

vagrant@ubuntu-xenial:~$ sudo chmod a+x /opt/puppetlabs/facter/facts.d/date.sh
vagrant@ubuntu-xenial:~$ sudo facter date
2020-02-11

LAB #4 - Itérations

Itération permet d'économiser du code.

Itération et Tableaux

Créez le fichier iteration_simple.pp

vagrant@ubuntu-xenial:~$ sudo vi iteration_simple.pp
vagrant@ubuntu-xenial:~$ cat iteration_simple.pp
file { '/usr/local/bin/task1':
  content => "echo I am task1\n",
  mode    => '0755',
}

file { '/usr/local/bin/task2':
  content => "echo I am task2\n",
  mode    => '0755',
}

file { '/usr/local/bin/task3':
  content => "echo I am task3\n",
  mode    => '0755',
}

Important - Dans ce manifest il y a trois ressources presque identiques, différenciées uniquement par le numéro de tâche ( task1, task2, task3 ).

Créez maintenant le fichier iteration_each.pp :

vagrant@ubuntu-xenial:~$ sudo vi iteration_each.pp
vagrant@ubuntu-xenial:~$ cat iteration_each.pp
$tasks = ['task1', 'task2', 'task3']
$tasks.each | $task | {
  file { "/usr/local/bin/${task}":
    content => "echo I am ${task}\n",
    mode    => '0755',
  }
}

Important - Dans ce manifest la fonction each crée une boucle qui prend pour chaque exécution une des valeurs du tableau $tasks ( task1, task2, task3 ).

La forme du boule each est :

ARRAY.each | ELEMENT | {
BLOCK
}

Important - Ici ARRAY est un tableau et ELEMENT est le nom d'une variable qui contiendra chaque valeur du tableau à tour de rôle. BLOCK est du code Puppet qui appelle l'ELEMENT.

Iteration et Hashes

Créez le fichier suivant :

vagrant@ubuntu-xenial:~$ sudo vi iteration_hash.pp
vagrant@ubuntu-xenial:~$ cat iteration_hash.pp
$nics = $facts['networking']['interfaces']
$nics.each | String $interface, Hash $attributes | {
  notice("Interface ${interface} has IP ${attributes['ip']}")
}

Appliquez le manifest :

vagrant@ubuntu-xenial:~$ sudo puppet apply iteration_hash.pp
Notice: Scope(Class[main]): Interface enp0s3 has IP 10.0.2.15
Notice: Scope(Class[main]): Interface lo has IP 127.0.0.1
Notice: Compiled catalog for ubuntu-xenial in environment production in 0.02 seconds
Notice: Applied catalog in 0.01 seconds

La liste des interfaces réseau retournée est un Hash nommé $nics.


Copyright © 2022 Hugh Norris.

Menu