Best PracticesBlogCI/CDContainerizationGitOpsPRIME Framework
Blueprint for Enterprise-Ready Network Automation Pipelines
Blueprint for Enterprise-Ready Network Automation Pipelines¶
This post is part of our ongoing series on network automation best practices, grounded in the PRIME Framework and PRIME Philosophy.
Transparency Note
Examples, scenarios, and any outcome figures in this article are provided for education and are based on enterprise delivery experience or anonymised composite scenarios unless explicitly identified as direct Nautomation Prime client outcomes.
Enterprise automation is more than scriptsβitβs pipelines, version control, and safe rollouts. This post covers how to build CI/CD, GitOps, and containerized pipelines for network automation, and how the PRIME Framework ensures safety and empowerment.
# scripts/change_approval.pyimportrequestsimportosimporttimeclassServiceNowChangeManager:def__init__(self):self.snow_url=os.environ['SERVICENOW_URL']self.snow_user=os.environ['SERVICENOW_USER']self.snow_pass=os.environ['SERVICENOW_PASS']defcreate_change_ticket(self,description,automation_type):"""Create a change ticket in ServiceNow."""headers={'Content-Type':'application/json'}auth=(self.snow_user,self.snow_pass)data={'short_description':description,'description':f'Automated {automation_type} deployment','category':'Network','subcategory':'Automation','cmdb_ci':'Network Infrastructure',}response=requests.post(f"{self.snow_url}/api/now/table/change_request",headers=headers,auth=auth,json=data)ifresponse.status_code==201:change=response.json()['result']return{'change_id':change['number'],'sys_id':change['sys_id']}else:raiseException(f"Failed to create change: {response.text}")defwait_for_approval(self,change_id,max_wait_minutes=30):"""Wait for change to be approved."""headers={'Accept':'application/json'}auth=(self.snow_user,self.snow_pass)start_time=time.time()max_wait_seconds=max_wait_minutes*60whiletime.time()-start_time<max_wait_seconds:response=requests.get(f"{self.snow_url}/api/now/table/change_request?number={change_id}",headers=headers,auth=auth)ifresponse.status_code==200:change=response.json()['result'][0]approval_state=change.get('approval','pending')ifapproval_state=='approved':returnTrueelifapproval_state=='rejected':raiseException(f"Change rejected: {change.get('close_notes')}")print(f"Change {change_id} waiting for approval...")time.sleep(30)# Check every 30 secondsraiseException(f"Change approval timeout after {max_wait_minutes} minutes")defclose_change(self,change_id,status='successful'):"""Close a change ticket."""headers={'Content-Type':'application/json'}auth=(self.snow_user,self.snow_pass)data={'state':'closed','close_code':'1'ifstatus=='successful'else'2','close_notes':f'Deployment completed: {status}'}response=requests.patch(f"{self.snow_url}/api/now/table/change_request?number={change_id}",headers=headers,auth=auth,json=data)returnresponse.status_code==200# Usage in GitHub Actionschange_mgr=ServiceNowChangeManager()change=change_mgr.create_change_ticket("Deploy VLAN config","network-config")print(f"Change created: {change['change_id']}")# Wait for approval (will block pipeline)change_mgr.wait_for_approval(change['change_id'])print("Change approved, proceeding with deployment")# Deploy...# Close the changechange_mgr.close_change(change['change_id'],status='successful')
Blue Environment (Old) Green Environment (New)
β Active β Inactive
β Traffic
After validation:
Blue Environment (Old) Green Environment (New)
β Inactive β Active
β Traffic
# scripts/blue_green_deploy.pyimportasynciofromnornirimportInitNornirasyncdefdeploy_blue_green(config_changes):"""Deploy with blue-green strategy."""nr=InitNornir(config_file="config.yaml")# Deploy to green environment (idle)print("Deploying to GREEN environment...")green_nr=InitNornir(config_file="config_green.yaml")green_nr.run(task=apply_config,config=config_changes)# Validate greenprint("Validating GREEN environment...")green_nr.run(task=validate_config)# Switch trafficprint("Switching traffic BLUE β GREEN...")switch_load_balancer_to_green()# Monitor greenawaitmonitor_health("green",duration=300)# 5 minutes# If all good, old blue can be decommissionedprint("Deployment successful!")asyncdefmonitor_health(environment,duration=300):"""Monitor health metrics for a period."""start=time.time()whiletime.time()-start<duration:health=check_health(environment)ifhealth['error_rate']>0.05:raiseException("High error rate detected, rolling back")awaitasyncio.sleep(10)
defdeploy_with_rollback(config_changes):"""Deploy config with automatic rollback."""nr=InitNornir(config_file="config.yaml")# Backup current configsprint("Backing up configurations...")nr.run(task=backup_config)# Apply changesprint("Applying configuration changes...")results=nr.run(task=apply_config,config=config_changes)# Validate changesprint("Validating changes...")validation=nr.run(task=validate_config)# Check for failuresifvalidation.failed_hosts:print(f"Validation failed on: {validation.failed_hosts}")# Auto-rollbackprint("Rolling back configuration...")nr.run(task=restore_config)# Restore from backup# Alert opssend_alert(f"Deployment failed and rolled back: {validation.failed_hosts}")returnFalsereturnTrue