Now we need to setup the pipline stages in the .gitlab-ci.yml
file. This file acts
as the control file for defining all CI/CD jobs.
We also need to define the test cases that will be used to validate changes to the staging and production environments.
When managing your network with IaC, adding good test cases is critical. Your test code should be equal to or better than the production code. The main objective for IaC is to validate the code and configuration on a staging environment before deploying it to the production environment. This validation can reduce the chance of an outage dramatically.
In this lab, we will use an Ansible validation playbook for the following:
NOTE: The validation we do as part of this lab is just a simple example. The validation needed in a real staging and production environment should include as many tests as required in order to ensure that what you are deploying to the production environment will not break your system.
---
- name: verify | check if all VRFs are deployed
block:
- name: verify | query all VRFs from {{ fabric.name }}
dcnm_rest:
path: "/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/top-down/fabrics/{{ fabric.name }}/vrfs"
method: GET
register: result
- name: verify | check if status is DEPLOYED
assert:
that:
- item.vrfStatus != "OUT-OF-SYNC"
quiet: true
loop: "{{ result.response.DATA }}"
tags:
- verify
- name: verify | check if all Networks are deployed
block:
- name: verify | query all Networks from {{ fabric.name }}
dcnm_rest:
path: "/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/top-down/fabrics/{{ fabric.name }}/networks"
method: GET
register: result
- name: verify | check if status is DEPLOYED
assert:
that:
- item.networkStatus != "OUT-OF-SYNC"
quiet: true
loop: "{{ result.response.DATA }}"
tags:
- verify
Examine the playbook above. We are using the dcnm_rest
module from the collection
to query the VRF's and Networks on NDFC and make sure they are deployed properly. The dcnm_rest module can be used
to call any REST API exposed by NDFC but we recommend using other higher level function modules in the collection
first if they are available for your particuler automation workflow.
We now want to add a new VRF and two more Networks that will automatically get deployed and verified in our staging environment when we trigger the CI pipeline in the next section.
Add the new content to the file using the following commands.
touch /home/cisco/CiscoLive/DEVWKS-3928/group_vars/all/overlay.yml
cat << EOF > /home/cisco/CiscoLive/DEVWKS-3928/group_vars/all/overlay.yml
---
vrfs:
- vrf_name: &refvrf_devnet vrf_devnet
vrf_id: 150001
vlan_id: 2000
attach_group: all_leaf
# -----------------------------------------------
# Insert the new VRF vrf_app1 below into the file
# -----------------------------------------------
- vrf_name: &refvrf_app1 vrf_app1
vrf_id: 50001
vlan_id: 2010
attach_group: all_leaf
networks:
- net_name: network_devnet1
vrf_name: *refvrf_devnet
net_id: 130001
vlan_id: 2301
vlan_name: network_devnet1_vlan2301
gw_ip_subnet: "10.10.10.1/24"
attach_group: esxi
- net_name: network_devnet2
vrf_name: *refvrf_devnet
net_id: 130002
vlan_id: 2302
vlan_name: network_devnet2_vlan2302
gw_ip_subnet: "10.10.11.1/24"
attach_group: esxi
# ------------------------------------------------------------------------
# Insert the new Networks network_web and network_app below into the file
# ------------------------------------------------------------------------
- net_name: network_web
vrf_name: *refvrf_app1
net_id: 30001
vlan_id: 2311
vlan_name: network_web_vlan2311
gw_ip_subnet: "10.1.1.1/24"
attach_group: esxi
- net_name: network_app
vrf_name: *refvrf_app1
net_id: 30002
vlan_id: 2312
vlan_name: network_web_vlan2312
gw_ip_subnet: "10.1.2.1/24"
attach_group: esxi
EOF
Similar to the top level build.yml playbook, we need a top level verify.yml playbook to drive the valiation tasks.
---
- name: verify | verify the fabric is in the correct state
hosts: ndfc
gather_facts: false
roles:
- verify
.gitlab-ci.yml
in the project root folder.
Create the file and add content to the file using the following commands.
touch /home/cisco/CiscoLive/DEVWKS-3928/.gitlab-ci.yml
cat << EOF > /home/cisco/CiscoLive/DEVWKS-3928/.gitlab-ci.yml
---
variables:
environ: "staging"
stages:
- lint
- deploy
- verify
ansible lint:
stage: lint
image:
name: "cytopia/ansible-lint:5"
entrypoint: [""]
rules:
- if: '\$CI_PIPELINE_SOURCE == "push" && \$CI_COMMIT_BRANCH == "stage"'
before_script:
- echo \$ANSIBLE_VAULT_PASSWORD > .vault_pass
- export ANSIBLE_PERSISTENT_COMMAND_TIMEOUT=1000
- export ANSIBLE_PERSISTENT_CONNECT_TIMEOUT=1000
- export no_proxy="localhost,127.0.0.1,172.25.74.47"
- python3 -m pip install requests
- echo \$ANSIBLE_VAULT_PASSWORD > .vault_pass
script:
- ansible-galaxy -vvv collection install -r collections/requirements.yml
- ansible-lint -p build.yml
- ansible-lint -p group_vars/all/overlay.yml
deploy_on_stage:
stage: deploy
image:
name: "cytopia/ansible"
entrypoint: [""]
rules:
- if: \$CI_PIPELINE_SOURCE == 'merge_request_event' && \$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main"
before_script:
- echo \$ANSIBLE_VAULT_PASSWORD > .vault_pass
- export ANSIBLE_PERSISTENT_COMMAND_TIMEOUT=1000
- export ANSIBLE_PERSISTENT_CONNECT_TIMEOUT=1000
- python3 -m pip install requests
script:
- ansible-playbook --version
- ansible-galaxy -vvv collection install -r collections/requirements.yml
- ansible-playbook -i hosts.stage.yml build.yml --vault-password-file .vault_pass
verify_on_stage:
stage: verify
image:
name: "cytopia/ansible"
entrypoint: [""]
rules:
- if: \$CI_PIPELINE_SOURCE == 'merge_request_event' && \$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main"
before_script:
- echo \$ANSIBLE_VAULT_PASSWORD > .vault_pass
- python3 -m pip install requests
script:
- sleep 10
- ansible-playbook --version
- ansible-galaxy -vvv collection install -r collections/requirements.yml
- ansible-playbook -i hosts.stage.yml verify.yml --vault-password-file .vault_pass
deploy_on_prod:
stage: deploy
image:
name: "cytopia/ansible"
entrypoint: [""]
rules:
- if: '\$CI_PIPELINE_SOURCE == "push" && \$CI_COMMIT_BRANCH == "main"'
before_script:
- echo \$ANSIBLE_VAULT_PASSWORD > .vault_pass
- export ANSIBLE_PERSISTENT_COMMAND_TIMEOUT=1000
- export ANSIBLE_PERSISTENT_CONNECT_TIMEOUT=1000
- python3 -m pip install requests
script:
- ansible-playbook --version
- ansible-galaxy -vvv collection install -r collections/requirements.yml
- ansible-playbook -i hosts.prod.yml build.yml --vault-password-file .vault_pass
verify_on_prod:
stage: verify
image:
name: "cytopia/ansible"
entrypoint: [""]
rules:
- if: '\$CI_PIPELINE_SOURCE == "push" && \$CI_COMMIT_BRANCH == "main"'
before_script:
- echo \$ANSIBLE_VAULT_PASSWORD > .vault_pass
- python3 -m pip install requests
script:
- sleep 10
- ansible-playbook --version
- ansible-galaxy -vvv collection install -r collections/requirements.yml
- ansible-playbook -i hosts.prod.yml verify.yml --vault-password-file .vault_pass
EOF
The pipeline file above defines 3 stages.
All of these stages run in a docker container that are built with the ansible-lint, ansible-playbook and ansible-galaxy binaries.
We will tigger the various pipeline stages in the next section.