Ansible CISCO CIMC : installation d’un firmware

L’installation d’un firmware sur une CIMC via la GUI n’est nativement pas possible (enfin pas totalement, on nuance un peu nos propos car il existe une façon de faire via le dépôt du firmware localement sur le serveur via SFTP (WinSCP est parfait pour ce type d’action) par exemple, puis de faire l’update de firmware via la GUI de la CIMC),  le downgrade est lui bien  possible ;). Si vous souhaitez upgrader le firmware d’un serveur CISCO vous pouvez aussi passer par la Cisco IMC Supervisor (via la GUI ou les APIs),  dans notre cas  nous souhaitions passer directement par la CIMC afin de simplifier (et éviter un spof) notre automatisation d’upgrade de fimware.  Dans le cas présent nous avons automatisé l’upgrade de firmware sur des serveurs CISCO (C220 m4/C240 m5) via un partage NFS (le CIFS est possible). Nous vous partageons le playbook ci-dessous qui permet l’upgrade/downgrade de firmware des serveurs CISCO (testé sur des C220 m4/C240 m5) .

Les pré-requis :

  • Un partage NFS (ou CIFS)
  • Le(s) firmware CISCO
  • Renseignez les variables imc_hostname, bundle_ver_file, nfsserver et nsf_remoteShare

et c’est tout bon 😉 .

---
- hosts: localhost
  gather_facts: false

  vars:
    imc_hostname: "YourServrer.fqdn"
    bundle_ver_file: "YouFirmwareISOFile.iso"
    nfsserver: "Yourserver.fqdn"
    nsf_remoteShare: "/vol_xxxyyyy/pathvol/{{ bundle_ver_file }}"

  tasks:      
    
    
    - name: Upgrade firmware on {{ imc_hostname }} - {{currentSite}}
      community.general.imc_rest:
        hostname: '{{ imc_hostname }}'
        username: '{{ imc_username }}'
        password: '{{ imc_password }}'
        validate_certs: no
        content: |
          <configConfMo>
            <inConfig> 
              <huuFirmwareUpdater dn='sys/huu/firmwareUpdater' adminState='trigger' 
                remoteIp='{{nsf_remoteIp}}' remoteShare='{{nsf_remoteShare}}'
                mapType='nfs' username='' password="" stopOnError='yes' 
                timeOut='200' verifyUpdate='yes' updateComponent='all' >
              </huuFirmwareUpdater>
            </inConfig>
          </configConfMo>
      register: CIMC_upgrade

    - debug:
        msg: "{{CIMC_upgrade}}"

    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
    # Check firmware status with block et rescue section
    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
    - name: Check status firmware Block
      block:
        # The delay is 300 with retrie 20 for to avoid losing the CIMC during the upgrade
        - name: Check status firmware ( !!!! retry 20 - delay 480 !!!! )
          community.general.imc_rest:
            hostname: '{{ imc_hostname }}'
            username: '{{ imc_username }}'
            password: '{{ imc_password }}'
            validate_certs: no
            content: |
              <configResolveClass inHierarchical="true" classId="huuFirmwareUpdateStatus" dn="sys/huu/firmwareUpdater/updateStatus"/>
          retries: 20
          delay: 480
          register: CIMC_upgrade_status
          until: "CIMC_upgrade_status.configResolveClass.children[0].outConfigs.children[0].huuFirmwareUpdateStatus.attributes.updateEndTime != 'NA'"

        - debug:
            msg: 
              - "{{CIMC_upgrade_status.configResolveClass.children[0].outConfigs.children[0].huuFirmwareUpdateStatus.attributes}}"

      rescue:
        - name: "***** RESCUE BLOCK ******"
          debug:
            msg: "****** RESCUE BLOCK Start **********"

        - name: RESCUE Pause for 10 minutes (wait reboot CIMC)
          ansible.builtin.pause:
            minutes: 10
        
        - name: RESCUE Check status firmware ( !!!! retry 20 - delay 600 !!!! )
          community.general.imc_rest:
            hostname: '{{ imc_hostname }}'
            username: '{{ imc_username }}'
            password: '{{ imc_password }}'
            validate_certs: no
            content: |
              <configResolveClass inHierarchical="true" classId="huuFirmwareUpdateStatus" dn="sys/huu/firmwareUpdater/updateStatus"/>
          retries: 20
          delay: 600
          register: CIMC_upgrade_status
          until: "CIMC_upgrade_status.configResolveClass.children[0].outConfigs.children[0].huuFirmwareUpdateStatus.attributes.updateEndTime != 'NA'"

        - debug:
            msg: 
              - "{{CIMC_upgrade_status.configResolveClass.children[0].outConfigs.children[0].huuFirmwareUpdateStatus.attributes}}"


    - name : Set variable fw_starttime and fw_endtime
      set_fact:
        fw_starttime: "{{CIMC_upgrade_status.configResolveClass.children[0].outConfigs.children[0].huuFirmwareUpdateStatus.attributes.updateStartTime}}"
        fw_endtime: "{{CIMC_upgrade_status.configResolveClass.children[0].outConfigs.children[0].huuFirmwareUpdateStatus.attributes.updateEndTime}}"


    - name: Display Start time and End time upgrade firmware
      debug:
        msg: 
          - "Start time : {{ fw_starttime }}"
          - "End time : {{ fw_endtime }}"
      when: "fw_starttime != 'NA' and fw_endtime != 'NA'"


    - name: Upgrade firmware in minutes
      debug:
        msg: "Number of minutes : {{ (((fw_endtime | to_datetime) - (fw_starttime | to_datetime)).seconds / 60 ) | round | int }}"
      when: "fw_starttime != 'NA' and fw_endtime != 'NA'"


    - name: Check firmware version
      community.general.imc_rest:
        hostname: '{{ imc_hostname }}'
        username: '{{ imc_username }}'
        password: '{{ imc_password }}'
        validate_certs: no
        content: |
          <configResolveClass inHierarchical="true" classId="firmwareRunning" />
      retries: 60
      delay: 120
      register: CIMC_upgrade_version


    - name: Set variable currentfirmware
      set_fact:
        currentfirmware: "{{CIMC_upgrade_version.configResolveClass.children[0].outConfigs.children[1].firmwareRunning.attributes.version}}"


    - name: Display current firmware on {{ imc_hostname }} after upgrade
      debug:
        msg: "Current firmware : {{ currentfirmware }}"

Une fois lancé,  le playbook va vous donner le retour du code HTTP (logiquement 200 si tout va bien), puis va checker toutes les 480 secondes (X20)  le statut de l’upgrade du firmware (cela permet de gérer l’inaccessibilité de la CIMC lors de son upgrade, si toutefois le check tombe pendant l’inaccessibilité de la CIMC alors ça passe via le block rescue et on recommence via un check toutes les 600 secondes x 20), une fois l’upgrade terminé vous aurez la liste des composants upgraded/skipped, le start time et le end time de l’upgrade (ainsi que la durée de l’upgrade/downgrade) et l’affichage de la version du firmware après upgrade/downgrade.

Enjoy 😉

Post to Twitter

Ansible CISCO CIMC : Reset CIMC

Cela faisait un moment que l’on souhaitait intégrer un reset CIMC dans nos workflows de type souscription/décommissionnement de host CISCO (afin de rendre la CIMC le plus “propre” au début du workflow). C’est désormais chose faite via l’ajout du playbook ci-dessous. 

  - hosts: localhost
  
    vars:
      imc_hostname: "your_IMC"
      imc_username: "IMC_username"
      imc_password: "IMC_password"
      
    tasks:
  
      - name: Reset CIMC
        ignore_errors: yes
        community.general.imc_rest:
          hostname: '{{ imc_hostname }}'
          username: '{{ imc_username }}'
          password: '{{ imc_password }}'
          validate_certs: no
          timeout: 30
          content: |
            <configConfMo>
              <inConfig>
                <computeRackUnit adminPower="bmc-reset-immediate" dn="sys/rack-unit-1">
                </computeRackUnit>
              </inConfig>
            </configConfMo>
        register: result
      
       - debug:
          var: result
        
       - name: Pause for 5 minutes to wait cimc reset
         ansible.builtin.pause:
           minutes: 5
         
       - name: Check uri CIMC
        uri:
          url: "{{ imc_hostname }}"
          validate_certs: no
          status_code: 200
        register: result_cimcuri
        retries: 5
        delay: 10
        until: result_cimcuri is not failed
  
  
      - name: Stop play if result is not 200
        ansible.builtin.fail:
          msg:  "!!! Please check uri {{ imc_hostname }} !!!"
        when: result_cimcuri.status != 200 
  
      - debug:
          msg: "{{ result_cimcuri }}"

Une fois la task lancée, après le time-out (quelle que soit la durée du time-out)  vous obtiendrez l’erreur ci-dessous (c’est pour cela que l’on a ajouté le “ignore_errors: yes” ).

msg”: “Task failed with error -1: Connection failure: The read operation timed out”

L’erreur n’empêche pas le reset CIMC et avec le “ignore_errors: yes” nous pouvons passer à la task suivante (nous avons ajouté une pause de 5 mn afin de laisser le temps à la CIMC de “remonter”), afin de vérifier que la CIMC est bien remontée on check l’uri de la CIMC via la task “Check uri CIMC”, si la CIMC ne répond (code retour http 200) alors la task “Stop play if result is not 200” arrête le play avec le message d’erreur “!!! Please check uri {{ imc_hostname }} !!!”

Généralement nous recherchons les call API de CIMC via l’url “http://CIMC/visore.html“, mais pour le reset de CIMC impossible de trouver le call API en question (au pire on pouvait passer en SSH sur la CIMC, mais ce n’était pas le but). C’est en googlelant que nous sommes tombés sur sur le call API permettant le reset CIMC : reset CIMC with call API

Post to Twitter

Playbook Ansible : ajouter un “Uplink profile” dans NSX-T

Dans NSX-T l’ajout d’un “Uplink Profil” est on ne peut plus simple via la GUI (quand celle-ci répond correctement 🙂 ), de notre côté on préfère (et de loin…) passer par les REST APIs, ces dernières sont plus sures et plus rapides surtout si on souhaite automatiser certaine actions. 

Vous l’avez remarquer on est dans un labs from scatch 🙂

Le playbook ci-dessous permet de créer un Uplink Profile, sans tag, avec un vlan en 102 et une mtu en 1600 dans un manager NSX-T (version 3.2.3.1.0).

Dans le playbook nous récupérons le nom et l’id du “Profile Uplink” afin de pouvoir les réutiliser plus tard si par exemple vous souhaitez aller plus loin dans vos actions (ajout d’un “Transport Node Profile par exemple etc…). Vous remarquerez que nous avons mis un block et un rescue afin de gérer proprement d’éventuelle(s) erreur(s),  si vous le souhaitez vous pouvez ajouter  des actions dans la partie rescue (comme une gestion de log par exemple 😉 ).

- hosts: localhost


  vars:
      nsx_manager: "Your_NSX-T-MANAGER"
      nsx_username: "admin"
      nsx_password: "PWD_admin"
      uplinkProfil_name: "Uplink_Profil01"
      mtuvar: "1600"
      vlanvar: "102"
  
          
  tasks:

    - name: Create a Uplink Profile
      block:
        - name: Create a Hostswitch Profile on NSX manager
          uri:
            url: "https://{{ nsx_manager }}/api/v1/host-switch-profiles"
            force_basic_auth: yes
            validate_certs: no
            headers:
              Accept: "application/json"
              Content-Type: "application/json"
            user: "{{ nsx_username }}"
            password: "{{ nsx_password }}"
            method: POST
            body: | 
              {
              "resource_type": "UplinkHostSwitchProfile",
              "display_name": "{{uplinkProfil_name}}",
              "mtu": "{{mtuvar}}",
              "teaming": {
                  "standby_list": [
                    {
                        "uplink_name": "uplink-2",
                        "uplink_type": "PNIC"
                    }
                  ],
                  "active_list": [
                    {
                        "uplink_name": "uplink-1",
                        "uplink_type": "PNIC"
                    }
                  ],
                  "policy": "FAILOVER_ORDER"
                },
                "transport_vlan": "{{vlanvar}}"
              }
            status_code: "201"
            body_format: json
          register: nsx_hostswitch


        - name: Display ID "Create a Hostswitch Profile"
          debug:
            msg: 
              - "display_name : {{ nsx_hostswitch.json.display_name }}"
              - "ID : {{ nsx_hostswitch.json.id }}"

          when: nsx_hostswitch.json.id is defined

      rescue:
        - name: Display error
          debug:
            msg: "Uplink Profile on {{ nsx_manager }} is not created"
Une fois le playblook terminé, il nous reste plus qu’à vérifier ce que ça donne dans notre Manager
Nous avons bien notre nouveau “Uplink Profile”

Post to Twitter

Raccourcis clavier (mac) dans une session à distance de type windows

Dans ce billet (qui va  nous servir de post-it 😉 ) nous allons énumérer les principaux raccourcis clavier Mac (certains de ces raccourcis sont compatibles sur clavier windows) au sein d’une session à distance sur un os de type windows. 

CommandeRaccourci clavier
ctrl-alt-delctrl + +
Imprimerctrl + p
Affichage du menu démarrer
Afficher l’explorateur⌘ + e
Démarrer-Exécuter⌘ + r
Verrouiller la session⌘ + l
Basculer la langue sur le clavierctrl + maj
Copierctrl + c
Couperctrl + x
Collerctrl +v
Tout sélectionnerctrl + a
Annulerctrl + z
Rétablirctrl + y
Ouvrir une nouvelle fenêtreou ctrl + n
Actualiséfn + F5 (ou ctrl + r)
Réduire toute les fenêtres⌘ + d
Passer d’une fenêtre à une autre + tab
zoomer/dé-zoomerctrl + roulette souris
Touche F1 à F12fn + touche F1 etc…
symbole { + (
symbole } + )
symbole [ctrl + + (
symbole ]ctrl + + )
symbole \ctrl + + !
symbole |ctrl + + §

Post to Twitter

Console Horizon : Incorrect credentials were entered

Cela faisait longtemps qu’on n’avait pas rencontré le fameux “bug ou feature”. C’est chose faite avec cette erreur rencontrée sur la Console Horizon VIEW (VMware Horizon 2111.1) que nous a partagé un de nos collègues sysadmin. Lorsque nous essayons de nous authentifier sur la console  Horizon VIEW, juste après le timeout de session de la Console, nous obtenons systématiquement l’erreur :

Incorrect credentials were entered

Afin de by-passer cette erreur un refresh de la page d’authentification suffit 😉

En regardant dans les logs du Connection Server (C:\ProgramData\VMware\VDM\logs\debug-yyyy-mm-dd-xxxxxx.txt) on observe plusieurs erreurs de type “[RestApiAuthFilter] Authentication failed” :

<ajp-nio-127.0.0.1-8009-exec-4> [RestApiAuthFilter] Authentication failed,  Unexpected fault: encrypt: Cannot decrypt: Key not supplied

On comprend que le call de l’API “RestApiAuthFilter” est à l’origine de notre erreur d’authentification et que ce dernier n’a pas de ticket valide pour exécuter la requête (c’est pour cela que le refresh de la page d’authentification permet de résoudre le problème).

Allez dans le Menu Settings- Global Settings et cliquez sur le bouton modify afin de pouvoir modifier le time-out de session API

Une fois la modification enregistrée, cette dernière est prise en compte immédiatement. Le problème d’authentification  après le timeout de session de console n’apparait plus (sauf au delà du timeout de session API 🙂 ). 

Comme nous sommes curieux, nous avons testé cette feature avec la version VMware Horizon 2212.

Y a du mieux avec la 2212, désormais on nous demande de refresh la page 🙂

Après “quelques” échanges (qui ont duré quatre semaines) avec le support VMware la réponse finale tombe (et honnêtement on ne l’avait pas vu) “RTFM” : https://docs.vmware.com/en/VMware-Horizon/8-2111.1/rn/vmware-horizon-8-21111-release-notes/index.html

2852439: When administrators try to access the Horizon console without closing the browser or opening a new session in another tab or reloading the page after leaving the interface idle on the Login Page for an extended period of time (longer than the value for Global Settings Timeout), they are not able to login even with correct credentials.

    Workaround: Open a new session in another tab or reload the login page.

Vous avouerez cette feature est vraiment super sympa 🙂 .

Post to Twitter

Horizon VIEW 2212 : Impossible de charger les domains sur la console d’administration

Sur une infra Horizon VIEW 2212 fraîchement installée, nous avons rencontré l’erreur ci-dessous, sur la page d’authentification de la console d’administration.

Page failed to load. Please refresh the browser to reload the page.

Outre l’erreur nous n’avons plus de Domains, sûrement une Feature 🙂

L’erreur se produit en utilisant l’IP ou un alias DNS du Connection Serveur, dans l’url de la page de la console d’administration Horizon VIEW (en localhost ou via le nom DNS du Connection Server le problème ne se posait pas). 

Au départ, on pensait à un problème d’Origin checking qu’on avait déjà traité dans le billet Horizon 7.10 error : warning Echec de la connexion, au final le problème venait du CORS (Cross-Origin Ressource Sharing), même si dans notre cas nous ne sommes pas derrière un LB, le problème reste identique 🙂 . Comme VMware fait bien les choses, la KB85801 permet de résoudre facilement cette erreur via la modification (et ou la création) du fichier “locked.properties” (situé dans c:\program files\vmware\VMware View\Server\sslgateway\conf\).

Rajoutez “portalHost=” avec l’entrée qui vous intéresse (dans notre cas l’IP du Connection Serveur), puis redémarrez le service “Composant VMware Horizon View Security Gateway”
On retrouve bien nos Domains 😉

Post to Twitter

Playbook Ansible : renouveler le certificat aut0-signé d’une Cisco IMC

Depuis un certain temps, nous rencontrons des “problèmes” d’expiration de certificat (auto-signé) de CIMC sur des C220 M4.  Renouveler le certificat auto-signé d’une CIMC est très simple, vous pouvez le faire via la GUI de la CIMC, en CLI ou via le Playbook que nous venons de mettre en place. L’avantage du Playbook est qu’il va récupérer les informations de l’ancien certificat afin de les reporter sur le nouveau certificat, puis il va afficher les informations du nouveau certificat afin de s’assurer que le nouveau certificat a bien une nouvelle date d’expiration, et bien sûr c’est plus rapide et on peut se faire une petite boucle histoire de le faire sur plusieurs CIMC 😉 .

Juste avant Noël, donc on peut légitiment espérer un vrai certificat sous le sapin pour le 25 au matin 🙂
- hosts: localhost
  
  tasks:      
    
    # Read current certificate
    - name: Read Current Certificate
      community.general.imc_rest:
        hostname: '{{ imc_hostname }}'
        username: '{{ imc_username }}'
        password: '{{ imc_password }}'
        validate_certs: no
        content: |
          <configResolveDn  inHierarchical="false" dn="sys/cert-mgmt/curr-cert">
          </configResolveDn>
      retries: 10
      delay: 5
      register: current_certif
      until: current_certif is not failed

    - debug:
        msg: "{{ current_certif }}"

    - name: create variables from current_certif
      set_fact:
        var_commonName: "{{ current_certif.configResolveDn.children[0].outConfig.children[0].currentCertificate.attributes.commonName }}"
        var_countryCode: "{{ current_certif.configResolveDn.children[0].outConfig.children[0].currentCertificate.attributes.countryCode }}"
        var_organization: "{{ current_certif.configResolveDn.children[0].outConfig.children[0].currentCertificate.attributes.organization }}"
        var_organizationalUnit: "{{ current_certif.configResolveDn.children[0].outConfig.children[0].currentCertificate.attributes.organizationalUnit }}"
        var_locality: "{{ current_certif.configResolveDn.children[0].outConfig.children[0].currentCertificate.attributes.locality }}"
        var_state: "{{ current_certif.configResolveDn.children[0].outConfig.children[0].currentCertificate.attributes.state }}"
        var_validFrom: "{{ current_certif.configResolveDn.children[0].outConfig.children[0].currentCertificate.attributes.validFrom }}"
        var_validTo: "{{ current_certif.configResolveDn.children[0].outConfig.children[0].currentCertificate.attributes.validTo }}"
        var_signatureAlgorithm: "SHA384"
        var_selfSigned: "yes"

    - debug:
        msg: 
          - "{{ var_commonName }}"
          - "{{ var_countryCode }}"
          - "{{ var_organization }}"
          - "{{ var_organizationalUnit }}"
          - "{{ var_locality }}"
          - "{{ var_state }}"
          - "{{ var_validFrom }}"
          - "{{ var_validTo }}"


     # Generate selfSigned Certificate
    - name: Generate selfSigned Certificate
      community.general.imc_rest:
        hostname: '{{ imc_hostname }}'
        username: '{{ imc_username }}'
        password: '{{ imc_password }}'
        validate_certs: no
        timeout: 180
        content: |
            <configConfMo dn="sys/cert-mgmt/gen-csr-req" inHierarchical="false">
            <inConfig>
            <generateCertificateSigningRequest commonName="{{ var_commonName }}" 
              organization="{{ var_organization }}" 
              organizationalUnit="{{ var_organizationalUnit }}" locality="{{ var_locality }}" 
              state="{{ var_state }}" countryCode="United States" 
              dn="sys/cert-mgmt/gen-csr-req" selfSigned="yes"/>
            </inConfig>
            </configConfMo>
      retries: 10
      delay: 5
      register: generate_certif
      until: generate_certif is not failed


    # Read the new certificate
    - name: Read the new certificate
      community.general.imc_rest:
        hostname: '{{ imc_hostname }}'
        username: '{{ imc_username }}'
        password: '{{ imc_password }}'
        validate_certs: no
        content: |
          <configResolveDn  inHierarchical="false" dn="sys/cert-mgmt/curr-cert">
          </configResolveDn>
      retries: 30
      delay: 5
      register: new_certif
      until: (new_certif is not failed) and (new_certif.configResolveDn.children[0].outConfig.children[0].currentCertificate.attributes.commonName == var_commonName)

    - debug:
        msg: "{{ new_certif }}"

    - name: create variables for new_certif
      set_fact:
        var_commonName: "{{ new_certif.configResolveDn.children[0].outConfig.children[0].currentCertificate.attributes.commonName }}"
        var_countryCode: "{{ new_certif.configResolveDn.children[0].outConfig.children[0].currentCertificate.attributes.countryCode }}"
        var_organization: "{{ new_certif.configResolveDn.children[0].outConfig.children[0].currentCertificate.attributes.organization }}"
        var_organizationalUnit: "{{ new_certif.configResolveDn.children[0].outConfig.children[0].currentCertificate.attributes.organizationalUnit }}"
        var_locality: "{{ new_certif.configResolveDn.children[0].outConfig.children[0].currentCertificate.attributes.locality }}"
        var_state: "{{ new_certif.configResolveDn.children[0].outConfig.children[0].currentCertificate.attributes.state }}"
        var_validFrom: "{{ new_certif.configResolveDn.children[0].outConfig.children[0].currentCertificate.attributes.validFrom }}"
        var_validTo: "{{ new_certif.configResolveDn.children[0].outConfig.children[0].currentCertificate.attributes.validTo }}"

    - debug:
        msg: 
          - "{{ var_commonName }}"
          - "{{ var_countryCode }}"
          - "{{ var_organization }}"
          - "{{ var_organizationalUnit }}"
          - "{{ var_locality }}"
          - "{{ var_state }}"
          - "{{ var_validFrom }}"
          - "{{ var_validTo }}"

Une fois le Playbook passé la CIMC a un nouveau certificat auto-signé avec les anciennes valeurs.

On est reparti pour 5 ans 🙂

Post to Twitter

Playbook Ansible : paramétrer l’état des disques dans une CIMC

Dans ce Playbook nous allons configurer l’état des disques d’un serveur CISCO M5 en “Unconfigured-good”  (via une CIMC), afin de les préparer pour la création d’un Virtual Drive  (voir notre précedent billet “Playbook Ansible : créer un Virtual Drive dans une CIMC“).

Les tasks du Playbook :

  • Check Power Status : récupère l’état du serveur (on/off ) envoie le résultat dans la variable  “SrvPwStatus“, si le serveur est “power off” alors le playbook s’arrête.
  • Check IMC uri : vérifie que l’url de la CIMC du serveur répond bien via un retour HTTP 200, si ce n’est pas le cas le Playblook s’arrête.
  • Search Status and ID storageLocalDisk 0 and 1 : récupère  le status des disques (JBOD/unconfigured-good) ainsi que l’ID de chaque disques. Le status des disques est envoyé dans les variables SearchStatusDisk0/SearchStatusDisk1, l’ID des disques  est envoyé dans les variables SearchIdDisk0/SearchIdDisk0
  • Search dn Storage Controller: permet de récupérer le dn du Storage Controller . Cette task est exécuté si un des disques à sa variable SearchStatusDisk0/SearchStatusDisk1 égal la variable StatusDiskJbod (“JBOD”)
  • Configure Disk 0 : Configure le disk0 en “make-unconfigured-good
    uniquement si la variable SearchStatusDisk0 est égale à StatusDiskJbod (“JBOD”)
  • Configure Disk 1 : Configure le disk1 en “make-unconfigured-good
    uniquement si la variable SearchStatusDisk1 est égale à StatusDiskJbod (“JBOD”)
    hosts: localhost
    
      vars:
        imc_hostname: 100.83.36.141
        imc_password: 
        imc_username: admin
        StatusDiskJbod: JBOD
        StatusDiskUnconfGood: unconfigured-good
        
    
      tasks:
    
        - name: Check Power Status
          community.general.imc_rest:
            hostname: '{{ imc_hostname }}'
            username: '{{ imc_username }}'
            password: '{{ imc_password }}'
            validate_certs: no
            content: |
              <configResolveClass inHierarchical="false" classId="computeRackUnit"/>      
          register: CIMCPower
        - set_fact:
            SrvPwStatus: "{{ CIMCPower.configResolveClass.children[0].outConfigs.children[0].computeRackUnit.attributes.operPower }} "
        - debug:
            msg: "{{ SrvPwStatus }}"
          failed_when: '"on" not in SrvPwStatus'
    
    
        - name: Check IMC uri
          uri:
            url: https://{{ imc_hostname }}
            validate_certs: no
            status_code: 200
          register: result
        - debug:
            msg: "{{result}}"
          failed_when: result.status != 200
            
    
        - name: Search Status and ID storageLocalDisk 0 and 1
          community.general.imc_rest:
            hostname: '{{ imc_hostname }}'
            username: '{{ imc_username }}'
            password: '{{ imc_password }}'
            validate_certs: no
            content: |
              <configResolveClass inHierarchical="true" classId="storageLocalDisk"/>
          register: storageLocalDisk
        - set_fact:
            SearchStatusDisk0: "{{ storageLocalDisk.configResolveClass.children[0].outConfigs.children[0].storageLocalDisk.attributes.pdState }}"
            SearchStatusDisk1: "{{ storageLocalDisk.configResolveClass.children[0].outConfigs.children[1].storageLocalDisk.attributes.pdState }}"
            SearchIdDisk0: "{{ 'pd-' + storageLocalDisk.configResolveClass.children[0].outConfigs.children[0].storageLocalDisk.attributes.id }}"
            SearchIdDisk1: "{{ 'pd-' + storageLocalDisk.configResolveClass.children[0].outConfigs.children[1].storageLocalDisk.attributes.id }}"
        - debug:
            msg: 
              - "{{ SearchStatusDisk0 }}"
              - "{{ SearchStatusDisk1 }}"
              - "{{ SearchIdDisk0 }}"
              - "{{ SearchIdDisk1 }}"
    
    
        - name: Search dn Storage Controller
          community.general.imc_rest:
            hostname: '{{ imc_hostname }}'
            username: '{{ imc_username }}'
            password: '{{ imc_password }}'
            validate_certs: no
            content: |
              <configResolveClass inHierarchical="true" classId="storageController"/>
          register: StorageController
          when: SearchStatusDisk0 == StatusDiskJbod  or SearchStatusDisk1 == StatusDiskJbod
        - set_fact:
            SearchDnRaid: "{{ StorageController.configResolveClass.children[0].outConfigs.children[0].storageController.attributes.dn }}"
          when: SearchStatusDisk0 == StatusDiskJbod or SearchStatusDisk1 == StatusDiskJbod
        - debug:
            msg: "{{ SearchDnRaid }}"
          when: SearchStatusDisk0 == StatusDiskJbod or SearchStatusDisk1 == StatusDiskJbod
    
    
        - name: Configure Disk 0
          community.general.imc_rest:
            hostname: '{{ imc_hostname }}'
            username: '{{ imc_username }}'
            password: '{{ imc_password }}'
            validate_certs: no
            content: |
              <configConfMo dn='{{SearchDnRaid}}/{{ SearchIdDisk0 }}'>
              <inConfig>
              <storageLocalDisk adminAction='make-{{StatusDiskUnconfGood}}'></storageLocalDisk>
              </inConfig>
              </configConfMo>
          retries: 3
          delay: 1
          when: SearchStatusDisk0 == StatusDiskJbod
    
        - name: Configure Disk 1
          community.general.imc_rest:
            hostname: '{{ imc_hostname }}'
            username: '{{ imc_username }}'
            password: '{{ imc_password }}'
            validate_certs: no
            content: |
              <configConfMo dn='{{SearchDnRaid}}/{{ SearchIdDisk1 }}'>
              <inConfig>
              <storageLocalDisk adminAction='make-{{StatusDiskUnconfGood}}'></storageLocalDisk>
              </inConfig>
              </configConfMo>
          retries: 3
          when: SearchStatusDisk1 == StatusDiskJbod
    
    Dans la CIMC nous avons quatre disques configurés en JBOD

    Il ne reste plus qu’à lancer le Playbook pour configurer les deux premiers disques en “unconfigured-good”.

    La task Check Power Status a bien vu le server “on”, le Playbook passe donc à la task suivante (dans le cas contraire le Playbook s’arrête)
    La task Check IMC uri récupère un “status 200”, le Playbook passe donc aux tasks qui configurent les disques (dans le cas contraire le Playbook s’arrête)
    Nos deux disques sont désormais configurés en “Unconfigured-good”
    C’est tout bon côté CIMC

    Post to Twitter

    Playbook Ansible : créer un Virtual Drive dans une CIMC

    Suite à un projet d’automatisation de déploiement de serveurs CISCO nous allons essayer de vous faire partager au fil du temps des Playbooks Ansible (essentiellement sur la partie CIMC dans un premier temps). Le playbook du jour permet la création d’un virtual drive dans une CIMC d’un serveur CISCO C220 M5 .

    - hosts: localhost
    
    
      vars:
        imc_hostname: ""
        imc_password: ""
        imc_username: "admin"
    
      tasks:
    
        - name: Search Disk Size
          community.general.imc_rest:
            hostname: '{{ imc_hostname }}'
            username: '{{ imc_username }}'
            password: '{{ imc_password }}'
            validate_certs: no
            content: |
              <configResolveClass inHierarchical="true" classId="storageLocalDisk"/>
          register: CIMC
        - set_fact:
            ddsize: "{{ CIMC.configResolveClass.children[0].outConfigs.children[0].storageLocalDisk.attributes.coercedSize }}"
        - debug:
            msg: "{{ ddsize }}"
    
        
        - name: Search dn Storage Controller
          community.general.imc_rest:
            hostname: '{{ imc_hostname }}'
            username: '{{ imc_username }}'
            password: '{{ imc_password }}'
            validate_certs: no
            content: |
              <configResolveClass inHierarchical="true" classId="storageController"/>
          register: StorageController
        - set_fact:
            SearchDnRaid: "{{ StorageController.configResolveClass.children[0].outConfigs.children[0].storageController.attributes.dn }}"
        - debug:
            msg: "{{ SearchDnRaid }}"
    
        
        - name: Create Virtual Drive
          community.general.imc_rest:
            hostname: '{{ imc_hostname }}'
            username: '{{ imc_username }}'
            password: '{{ imc_password }}'
            validate_certs: no
            content: |
              <configConfMo dn='{{SearchDnRaid}}/virtual-drive-create'> 
              <inConfig> 
              <storageVirtualDriveCreatorUsingUnusedPhysicalDrive virtualDriveName='vdTest' raidLevel='1' size="{{ddsize}}" driveGroup='[1,2]' writePolicy='Write Through' adminState='trigger'/>
              </inConfig>
              </configConfMo>
    

    Dans la task “Search Disk Size” on récupère la taille du premier disque qui nous servira à la création du RAID dans la task “Create Virtual Drive”. La task “Search dn Storage Controller” permet de récupérer le dn du Controller afin de pouvoir la récupérer dans la task “Create Virtual Drive” (si vous avez des Controller différents notamment)

    Notre CIMC sans Virtual Drive
    dans le Playbook on voit bien la taille des disques remontés et le dn qui seront utiles pour la création du Virtual Drive
    Le Virtual Drive est bien créé une fois le Playbook terminé

    Quelques lien sur les APi (REST/XML) CISCO IMC  :

    Post to Twitter

    VMmark 3.1.1 erreur lors du lancement de notre premier “Tile”

    Dernièrement nous avons testé VMmark 3.1.1 (pour rappel VMmark permet de bencher et mesurer l‘évolution des plates-formes virtuelles) afin de bencher des serveurs DELL (PowerEdge R7515). On vous passe les étapes douloureuses (nous ferons un billet sur l’installation de VMmark 3.1.1 très prochainement) de l’installation et la configuration. Une fois l’installation terminée on lance notre premier Tile et là une belle erreur (ce n’est pas comme si c’était la première en plus…) mais celle-là aurait pu être évitée (genre RTFM) : “Error : machine = client0: Invalid Trust Level (3).

    Ca commence pas bien notre affaire 🙂

    La seule info qui nous intéresse dans le message d’erreur est “Client0”, comme tout bon Sysadmin on fait un ping de notre fameux “Client0”, comme nous n’avons pas de retour, direction le vCenter.

    Sans IP elle ne risque pas de répondre notre CLIENT0 🙂

    En allant plus loin on constate que l’IP du CLIENT0 (192.168.1.2) a été attribué à la VM DeployVM0 (le conflit d’IP explique notre message d’erreur). Direction le fichier VMmark3.properties (situé dans /root/VMmark3 de la VM “PrimeClient”) afin de voir si on n’a pas un problème de configuration sur la VM “Client0” ou  DeployVM0. Après une recherche dans le fichier VMmark3.properties on comprend d’où vient le problème, le deploy/DeployVMinfo est égal à DeployVM0:192.168.2 soit l’IP de de notre VM “CLIENT0”. 

    Il ne reste plus qu’à mettre la bonne IP

    Quand on lit le commentaire “Specifies the name and IP address of the deployed VMs. Ex DeployVM0:192.168.1.245,DeployVM1:192.168.1.246” on se dit qu’on a raté quelques chose 🙂 .

    Une fois l’IP de la VM “DeployVM0” passer sur 192.168.1.245 notre Tile est passé sans encombre

    Post to Twitter