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.
In Gitlab, you need to review and define the variables that will be used in the CI/CD pipeline as environment variables.
These variables are used to store sensitive information such as usernames and passwords, which should not be hardcoded
in the pipeline file and are similar to in usage as to the secrets.sh
file you created in the previous part of the lab but did not store in git.
To save you time, several variables have been pre-defined. Your task is to add the remaining variable needed.
Variable (default)
All (default)
Masked
Unchecked
Checked
ND_PASSWORD
cisco.123
The pipeline defines the stages of the CI/CD workflow. Some stages should be triggered when a PR is created, and some
should be triggered when the code is merged. Different VCSs have different methods to define the pipeline.
Since we are using Gitlab, the pipeline stages are defined in file .gitlab-ci.yml
in the project root folder.
touch ~/workspace/ndfclab/nac/.gitlab-ci.yml
code-server -r ~/workspace/ndfclab/nac/.gitlab-ci.yml
---
image:
name: mtarking/ubuntu:22.04
variables:
FF_SCRIPT_SECTIONS: true
ANSIBLE_FORCE_COLOR: true
ANSIBLE_PERSISTENT_COMMAND_TIMEOUT: 1000
ANSIBLE_PERSISTENT_CONNECT_TIMEOUT: 1000
ND_USERNAME:
description: "Cisco ND Username"
ND_PASSWORD:
description: "Cisco ND Password"
NDFC_SW_USERNAME:
description: "Cisco NDFC Switch Username"
NDFC_SW_PASSWORD:
description: "Cisco NDFC Switch Password"
stages:
- lint
- deploy
- test
yamllint:
stage: lint
rules:
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
when: never
- if: ($CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main")
- if: $CI_COMMIT_BRANCH
before_script:
- python3 -m pip install -r requirements.txt
script:
- echo "Checking YAML files..."
- |-
if [[ "$CI_PIPELINE_SOURCE" == "merge_request_event" && "$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" == "main" ]] || \
[[ -n "$CI_COMMIT_BRANCH" ]]; then
set -o pipefail && yamllint -d relaxed ./host_vars/fabric-stage |& tee yamllint_fabric_stage.txt
fi
- |-
if [[ "$CI_PIPELINE_SOURCE" == "merge_request_event" && "$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" == "main" ]] || \
[[ -n "$CI_COMMIT_BRANCH" ]]; then
set -o pipefail && yamllint -d relaxed ./host_vars/fabric-external-stage |& tee yamllint_stage_external.txt
fi
- |-
if [[ "$CI_COMMIT_BRANCH" == "main" ]]; then
set -o pipefail && yamllint -d relaxed ./host_vars/fabric-prod |& tee yamllint_fabric_prod.txt
fi
- |-
if [[ "$CI_COMMIT_BRANCH" == "main" ]]; then
set -o pipefail && yamllint -d relaxed ./host_vars/fabric-external-prod |& tee yamllint_prod_external.txt
fi
- set -o pipefail && yamllint -d relaxed ./vxlan.yml |& tee yamllint_vxlan.txt
artifacts:
when: on_failure
paths:
- ./yamllint_*.txt
ansible_lint:
stage: lint
rules:
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
when: never
- if: ($CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main")
- if: $CI_COMMIT_BRANCH
before_script:
- python3 -m pip install -r requirements.txt
- ansible-galaxy collection install -r requirements.yml
script:
- set -o pipefail && ansible-lint -p vxlan.yml |& tee ansible_lint_vxlan.txt
artifacts:
when: always
paths:
- ./ansible_lint_*.txt
nac_validate:
stage: lint
rules:
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
when: never
- if: ($CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main")
- if: $CI_COMMIT_BRANCH
before_script:
- python3 -m pip install -r requirements.txt
- ansible-galaxy collection install -r requirements.yml
script:
- |-
if [[ "$CI_PIPELINE_SOURCE" == "merge_request_event" && "$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" == "main" ]] || \
[[ -n "$CI_COMMIT_BRANCH" ]]; then
set -o pipefail && ansible-playbook -i hosts.stage.yml -l fabric-stage validate.yml |& tee nac_validate_fabric_stage.txt
fi
- |-
if [[ "$CI_PIPELINE_SOURCE" == "merge_request_event" && "$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" == "main" ]] || \
[[ -n "$CI_COMMIT_BRANCH" ]]; then
set -o pipefail && ansible-playbook -i hosts.stage.yml -l fabric-external-stage validate.yml |& tee nac_validate_fabric_stage.txt
fi
- |-
if [[ "$CI_COMMIT_BRANCH" == "main" ]]; then
set -o pipefail && ansible-playbook -i hosts.prod.yml -l fabric-prod validate.yml |& tee nac_validate_fabric_prod.txt
fi
- |-
if [[ "$CI_COMMIT_BRANCH" == "main" ]]; then
set -o pipefail && ansible-playbook -i hosts.prod.yml -l fabric-external-prod validate.yml |& tee nac_validate_fabric_prod.txt
fi
artifacts:
when: always
paths:
- ./nac_validate_*.txt
deploy_stage:
stage: deploy
needs:
- yamllint
- ansible_lint
- nac_validate
rules:
- if: ($CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main")
before_script:
- python3 -m pip install -r requirements.txt
- ansible-galaxy collection install -r requirements.yml
script:
- set -o pipefail && ansible-playbook -i hosts.stage.yml vxlan.yml -l fabric-stage |& tee deploy_fabric_stage.txt
artifacts:
when: always
paths:
- ./deploy_*.txt
deploy_stage_external:
stage: deploy
needs:
- yamllint
- ansible_lint
- nac_validate
rules:
- if: ($CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main")
before_script:
- python3 -m pip install -r requirements.txt
- ansible-galaxy collection install -r requirements.yml
script:
- set -o pipefail && ansible-playbook -i hosts.stage.yml vxlan.yml -l fabric-external-stage |& tee deploy_stage_external.txt
artifacts:
when: always
paths:
- ./deploy_*.txt
test_stage:
stage: test
needs:
- deploy_stage
rules:
- if: ($CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main")
before_script:
- python3 -m pip install -r requirements.txt
- ansible-galaxy collection install -r requirements.yml
script:
- set -o pipefail && ansible-playbook -i hosts.stage.yml test.yml -l fabric-stage |& tee test_fabric_stage.txt
artifacts:
when: always
paths:
- ./test_*.txt
deploy_prod:
stage: deploy
needs:
- yamllint
- ansible_lint
- nac_validate
rules:
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "main"'
before_script:
- python3 -m pip install -r requirements.txt
- ansible-galaxy collection install -r requirements.yml
script:
- set -o pipefail && ansible-playbook -i hosts.prod.yml vxlan.yml -l fabric-prod |& tee deploy_fabric_prod.txt
artifacts:
when: always
paths:
- ./deploy_*.txt
deploy_prod_external:
stage: deploy
needs:
- yamllint
- ansible_lint
- nac_validate
rules:
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "main"'
before_script:
- python3 -m pip install -r requirements.txt
- ansible-galaxy collection install -r requirements.yml
script:
- set -o pipefail && ansible-playbook -i hosts.prod.yml vxlan.yml -l fabric-external-prod |& tee deploy_prod_external.txt
artifacts:
when: always
paths:
- ./deploy_*.txt
test_prod:
stage: test
needs:
- deploy_prod
rules:
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "main"'
before_script:
- python3 -m pip install -r requirements.txt
- ansible-galaxy collection install -r requirements.yml
script:
- set -o pipefail && ansible-playbook -i hosts.prod.yml test.yml -l fabric-prod |& tee test_fabric_prod.txt
artifacts:
when: always
paths:
- ./test_*.txt
All of these stages run in a docker container that are built with the ansible-lint, ansible-playbook and ansible-galaxy binaries.
You will replace the contents of the requirements.yml
file to include the the cisco.nac_dc_vxlan collection which
contains the VXLAN as Code roles.
touch ~/workspace/ndfclab/nac/requirements.yml
cat << EOF > ~/workspace/ndfclab/nac/requirements.yml
collections:
- name: community.general
version: 10.6.0
- name: ansible.posix
version: 2.0.0
- name: ansible.utils
version: 6.0.0
- name: ansible.netcommon
version: 8.0.0
- name: cisco.dcnm
version: 3.8.0
- name: cisco.nac_dc_vxlan
version: 0.4.2
EOF
Continue to the next section to create the tests needed for the end of the pipeline..