DevOps est une nouvelle façon d’approcher la manière dont on conçoit, on exploite et on délivre les services IT. J’ai eu souvent l’occasion de le dire et de l’écrire, mais réaliser des programmes ne fait pas du programmeur un DevOps et encore moins de la société qui l’emploie une société DevOps.
Entrer dans le monde DevOps implique de focaliser sur les objectifs, les personnes, les processus et les outils à mettre en oeuvre. Cet article se voulant plutôt orienté technique, il focalisera sur des éléments appartenant aux domaines des processus et des outils. Dans les processus DevOps, l’approche CI/CD (Continuous Integration / Continuous Delivery ou Deployment) est fondamentale. Le schéma proposé ci-dessus illustre le pipeline du « Continuous Integration » et met en évidence la différence subtile entre « Continuous Delivery » et « Continuous Deployment » et les deux premières étapes vont être expliquées.
Généralement associé à un gestionnaire de versioning, on utilise un outil d’intégration continue comme Jenkins, CircleCI ou l’intégration continue disponible dans GitLab pour mettre en place le pipeline proposé dans l’illustration de cet article.
Les tests syntaxique (lint) sont les plus simple à mettre en place. Pour le langage Python, il en existe plusieurs et Pylint en est un exemple que j’utilise souvent. Prenons l’exemple du code suivant et appliquons Pylint configuré par défaut dessus. Nous pouvons voir un certains nombre d’erreurs syntaxiques, qui même si elles ne semblent que cosmétiques, méritent d’être corrigées.
#!/usr/bin/env python import nornir import nornir.plugins.tasks.networking as nptn import nornir.plugins.tasks.text as nptt import nornir.plugins.functions.text as npft import nornir.core.filter as ncf def arista_configuration(task): template = task.run( task=nptt.template_file, template='configuration.j2', path='templates/' ) task.host['config'] = template.result results = task.run( task=nptn.napalm_configure, configuration=task.host['config'] ) def arista_config_template(): nr = nornir.InitNornir(config_file='config.yaml') veos = nr.filter(ncf.F(groups__contains='veos')) npft.print_title('Playbook to configure virtual switch with template') results = veos.run(task=arista_configuration) npft.print_result(results) if __name__ == '__main__': arista_config_template()
Une fois le code corrigé en fonction des recommandations, voici les améliorations apportées.
#!/usr/bin/env python """ Configure Arista vEOS-lab with a template """ import nornir import nornir.plugins.tasks.networking as nptn import nornir.plugins.tasks.text as nptt import nornir.plugins.functions.text as npft import nornir.core.filter as ncf def arista_configuration(task): """ Build and apply template """ template = task.run( task=nptt.template_file, template='configuration.j2', path='templates/' ) task.host['config'] = template.result task.run( task=nptn.napalm_configure, configuration=task.host['config'] ) def arista_config_template(): """ Run nornir task """ nornir_init = nornir.InitNornir(config_file='config.yaml') veos = nornir_init.filter(ncf.F(groups__contains='veos')) npft.print_title('Playbook to configure virtual switch with template') results = veos.run(task=arista_configuration) npft.print_result(results) if __name__ == '__main__': arista_config_template()
Le TDD (Test Driven Development) est une technique de développement mêlant intimement l’écriture des tests unitaires, la programmation et l’amélioration continue du code (encore appelée refactorisation). Idéalement, chaque fonction unitaire de l’application possède son propre test unitaire, écrit avant le code de la fonction. Le test est écrit dans un premier temps pour échouer, et le développeur s’assure ainsi en écrivant le test des conditions de réussite, mais aussi d’échec, de la fonction. Pour le langage Python, le framework Pytest est mon choix de prédilection.
[pytest] addopts = -p no:warnings log_cli = true
#!/usr/bin/env python """ Check Arista vEOS-lab configuration """ import nornir import nornir.plugins.tasks.networking as nptn import nornir.core.filter as ncf NORNIR_INIT = nornir.InitNornir(config_file='config.yaml') VEOS = NORNIR_INIT.filter(ncf.F(groups__contains='veos')) RESULTS = VEOS.run( task=nptn.napalm_get, getters=['interfaces', 'lldp_neighbors', 'environment', 'facts']) def test_interfaces_status(): """ Check interfaces status """ for res in RESULTS.values(): assert res.result['interfaces']['Port-Channel1']['is_up'] assert res.result['interfaces']['Port-Channel1']['is_enabled'] def test_lldp_neighbors(): """ Check number of lldp neighbors """ for res in RESULTS.values(): assert len(res.result['lldp_neighbors']) == 2 def test_cpu_threshold(): """ Check that CPU threshold is under 20% """ for res in RESULTS.values(): assert res.result['environment']['cpu'][0]['%usage'] < 20 def test_switch_version(): """ Check that release is 4.22 train """ for res in RESULTS.values(): assert res.result['facts']['os_version'][0:4] == '4.22'