Adding upstream version 1.3.1+dfsg.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
455a3d9fdb
commit
d2e39936a0
95 changed files with 10747 additions and 0 deletions
67
docs/labs/README.md
Normal file
67
docs/labs/README.md
Normal file
|
@ -0,0 +1,67 @@
|
|||
# cvprac labs
|
||||
|
||||
The following lab examples will walk through the most commonly used REST API calls using cvprac
|
||||
to help users interact with Arista CloudVision easily and automate the provisioning of network devices.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Authentication](#authentication)
|
||||
- [Password Authentication](#password-authentication)
|
||||
- [Service Account Token Authentication](#service-account-token-authentication)
|
||||
1. [Known Limitations](#known-limitations)
|
||||
|
||||
## Authentication
|
||||
|
||||
There are two ways to authenticate using the REST APIs:
|
||||
|
||||
- user/password (on-prem only)
|
||||
- service account token (available on CVP 2020.3.0+ and CVaaS)
|
||||
|
||||
### Password Authentication
|
||||
|
||||
```python
|
||||
from cvprac.cvp_client import CvpClient
|
||||
clnt = CvpClient()
|
||||
clnt.connect(['10.83.13.33'],'cvpadmin', 'arastra')
|
||||
```
|
||||
|
||||
### Service Account Token Authentication
|
||||
|
||||
To access the CloudVision as-a-Service and send API requests, "Service Account Token" is needed.
|
||||
After obtaining the service account token, it can be used for authentication when sending API requests.
|
||||
|
||||
Service accounts can be created from the Settings page where a service token can be generated as seen below:
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
The token should be copied and saved to a file that can later be referred to.
|
||||
|
||||
```python
|
||||
from cvprac.cvp_client import CvpClient
|
||||
clnt = CvpClient()
|
||||
with open("token.tok") as f:
|
||||
token = f.read().strip('\n')
|
||||
clnt.connect(nodes=['www.arista.io'], username='', password='', is_cvaas=True, api_token=token)
|
||||
```
|
||||
|
||||
>NOTE In case of CVaaS the `is_cvaas` parameters has to be set to `True`
|
||||
|
||||
Service accounts are supported on CVP on-prem starting from `2020.3.0`. More details in the [TOI](https://eos.arista.com/toi/cvp-2020-3-0/service-accounts/) and the [CV config guide](https://www.arista.com/en/cg-cv/cv-service-accounts).
|
||||
|
||||
```python
|
||||
from cvprac.cvp_client import CvpClient
|
||||
|
||||
with open("token.tok") as f:
|
||||
token = f.read().strip('\n')
|
||||
|
||||
clnt = CvpClient()
|
||||
clnt.connect(nodes=['10.83.13.33'], username='',password='',api_token=token)
|
||||
```
|
||||
|
||||
## Known Limitations
|
||||
|
||||
- for any APIs that interact with EOS devices, the service account name must match the name of the username
|
||||
configured on EOS and CVP
|
||||
- Support for REST API bindings for the Resource APIs (Lab 8) was added in CVP 2021.1.0
|
17
docs/labs/lab01-cvp-info/get_cvp_info.py
Normal file
17
docs/labs/lab01-cvp-info/get_cvp_info.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision
|
||||
clnt = CvpClient()
|
||||
clnt.connect(nodes=['cvp1'], username="username",password="password")
|
||||
|
||||
result = clnt.api.get_cvp_info()
|
||||
print(result)
|
57
docs/labs/lab02-inventory-operations/compliance_check.py
Normal file
57
docs/labs/lab02-inventory-operations/compliance_check.py
Normal file
|
@ -0,0 +1,57 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
### Compliance Code description
|
||||
compliance = {"0000":"Configuration is in sync",
|
||||
"0001": "Config is out of sync",
|
||||
"0002": "Image is out of sync",
|
||||
"0003": "Config & image out of sync",
|
||||
"0004": "Config, Image and Device time are in sync",
|
||||
"0005": "Device is not reachable",
|
||||
"0006": "The current EOS version on this device is not supported by CVP. Upgrade the device to manage.",
|
||||
"0007": "Extensions are out of sync",
|
||||
"0008": "Config, Image and Extensions are out of sync",
|
||||
"0009": "Config and Extensions are out of sync",
|
||||
"0010": "Image and Extensions are out of sync",
|
||||
"0011": "Unauthorized User",
|
||||
"0012": "Config, Image, Extension and Device time are out of sync",
|
||||
"0013": "Config, Image and Device time are out of sync",
|
||||
"0014": "Config, Extensions and Device time are out of sync",
|
||||
"0015": "Image, Extensions and Device time are out of sync",
|
||||
"0016": "Config and Device time are out of sync",
|
||||
"0017": "Image and Device time are out of sync",
|
||||
"0018": "Extensions and Device time are out of sync",
|
||||
"0019": "Device time is out of sync"
|
||||
}
|
||||
|
||||
# Create connection to CloudVision using Service account token
|
||||
with open("token.tok") as f:
|
||||
token = f.read().strip('\n')
|
||||
|
||||
clnt = CvpClient()
|
||||
clnt.connect(nodes=['cvp1'], username='',password='',api_token=token)
|
||||
|
||||
def check_devices_under_container(client, container):
|
||||
''' container is the container ID '''
|
||||
|
||||
nodeId = container['key']
|
||||
nodeName = container['name']
|
||||
api = '/ztp/getAllNetElementList.do?'
|
||||
queryParams = "nodeId={}&queryParam=&nodeName={}&startIndex=0&endIndex=0&contextQueryParam=&ignoreAdd=false&useCache=true".format(nodeId, nodeName)
|
||||
return client.get(api + queryParams)
|
||||
|
||||
|
||||
container = clnt.api.get_container_by_name('TP_LEAFS')
|
||||
|
||||
devices = (check_devices_under_container(clnt,container))
|
||||
|
||||
for device in devices['netElementList']:
|
||||
code = device['complianceCode']
|
||||
print(device['fqdn'], ' ', code,' ', compliance[code])
|
31
docs/labs/lab02-inventory-operations/get_running_configs.py
Normal file
31
docs/labs/lab02-inventory-operations/get_running_configs.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision
|
||||
clnt = CvpClient()
|
||||
clnt.connect(nodes=['cvp1'], username="username",password="password")
|
||||
|
||||
# Get the full inventory
|
||||
inventory = clnt.api.get_inventory()
|
||||
|
||||
# Create a list of MAC addresses
|
||||
device_macs = []
|
||||
for i in inventory:
|
||||
device_macs.append(i['systemMacAddress'])
|
||||
|
||||
# Create a dictionary with MAC to running-config mapping
|
||||
running_configs = {}
|
||||
for i in device_macs:
|
||||
running_configs[i] = clnt.api.get_device_configuration(i)
|
||||
|
||||
# Write the running-configs of each device using the hostname as the filename
|
||||
for i in inventory:
|
||||
with open(i['fqdn']+'.cfg', 'w') as f:
|
||||
f.write(running_configs[i['systemMacAddress']])
|
|
@ -0,0 +1,34 @@
|
|||
from cvprac.cvp_client import CvpClient
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
clnt = CvpClient()
|
||||
clnt.connect(nodes=['cvp1'], username="username",password="password")
|
||||
|
||||
ts = "2021-11-19T15:04:05.0Z" # rfc3339 time
|
||||
uri = "/api/v3/services/compliancecheck.Compliance/GetConfig"
|
||||
|
||||
# Fetch the inventory
|
||||
inventory = clnt.api.get_inventory()
|
||||
|
||||
# Iterate through all devices and get the running-config at the specified time for each device
|
||||
for device in inventory:
|
||||
sn = device['serialNumber']
|
||||
data = {"request":{
|
||||
"device_id": sn,
|
||||
"timestamp": ts,
|
||||
"type":"RUNNING_CONFIG"
|
||||
}
|
||||
}
|
||||
try:
|
||||
resultRunningConfig = clnt.post(uri, data=data)
|
||||
for idx in resultRunningConfig:
|
||||
if 'config' in idx:
|
||||
result = idx['config']
|
||||
break
|
||||
with open(device['hostname']+'.cfg','w') as f:
|
||||
f.write(result)
|
||||
except Exception as e:
|
||||
print("Not able to get configuration for device {} - exception {}".format(device['fqdn'], e))
|
|
@ -0,0 +1,30 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision
|
||||
clnt = CvpClient()
|
||||
clnt.connect(nodes=['cvp1'], username="username",password="password")
|
||||
|
||||
inventory = clnt.api.get_inventory()
|
||||
|
||||
devices = []
|
||||
for netelement in inventory:
|
||||
devices.append(netelement['systemMacAddress'])
|
||||
|
||||
# Remove devices from provisioning
|
||||
# This is a legacy API call that removes the devices from Network Provisioning
|
||||
# in CVP versions older than 2021.3.0, however it does not remove them from
|
||||
# the Device Inventory as that requires the streaming agent (TerminAttr) to be shutdown,
|
||||
# which this API does not support.
|
||||
# To fully decommission a device the device_decommissioning() API can be used, which is
|
||||
# supported from 2021.3.0+.
|
||||
# Note that using the delete_devices() function post CVP 2021.3.0 the device will be
|
||||
# automatically added back to the Undefined container.
|
||||
clnt.api.delete_devices(devices)
|
|
@ -0,0 +1,32 @@
|
|||
# Copyright (c) 2022 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import ssl
|
||||
import uuid
|
||||
import time
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision
|
||||
clnt = CvpClient()
|
||||
clnt.connect(nodes=['cvp1'], username="username", password="password")
|
||||
|
||||
device_id = input("Serial number of the device to be decommissioned: ")
|
||||
request_id = str(uuid.uuid4())
|
||||
clnt.api.device_decommissioning(device_id, request_id)
|
||||
|
||||
# This API call will fully decommission the device, ie remove it from both
|
||||
# Network Provisioning and Device Inventory (telemetry). It send an eAPI request
|
||||
# to EOS to shutdown the TerminAttr daemon, waits for streaming to stop and then removes
|
||||
# the device from provisioning and finally decommissions it. This operation can take a few minutes.
|
||||
# Supported from CVP 2021.3.0+ and CVaaS.
|
||||
decomm_status = "DECOMMISSIONING_STATUS_SUCCESS"
|
||||
decomm_result = ""
|
||||
while decomm_result != decomm_status:
|
||||
decomm_result = clnt.api.device_decommissioning_status_get_one(request_id)['value']['status']
|
||||
time.sleep(10)
|
||||
|
||||
print(decomm_result)
|
|
@ -0,0 +1,32 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision
|
||||
clnt = CvpClient()
|
||||
clnt.connect(nodes=['cvp1'], username="username",password="password")
|
||||
|
||||
# Get devices in a specific container
|
||||
inventory = clnt.api.get_devices_in_container("Undefined")
|
||||
|
||||
# Create device list
|
||||
devices = []
|
||||
for netelement in inventory:
|
||||
devices.append(netelement['systemMacAddress'])
|
||||
|
||||
# Remove devices from provisioning
|
||||
# This is a legacy API call that removes the devices from Network Provisioning
|
||||
# in CVP versions older than 2021.3.0, however it does not remove them from
|
||||
# the Device Inventory as that requires the streaming agent (TerminAttr) to be shutdown,
|
||||
# which this API does not support.
|
||||
# To fully decommission a device the device_decommissioning() API can be used, which is
|
||||
# supported from 2021.3.0+.
|
||||
# Note that using the delete_devices() function post CVP 2021.3.0 the device will be
|
||||
# automatically added back to the Undefined container.
|
||||
clnt.api.delete_devices(devices)
|
|
@ -0,0 +1,30 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision using Service account token
|
||||
with open("token.tok") as f:
|
||||
token = f.read().strip('\n')
|
||||
|
||||
clnt = CvpClient()
|
||||
clnt.connect(nodes=['cvp1'], username='', password='', api_token=token)
|
||||
|
||||
devices = ["50:08:00:a7:ca:c3","50:08:00:b1:5b:0b","50:08:00:60:c6:76",
|
||||
"50:08:00:25:9d:36","50:08:00:8b:ee:b1","50:08:00:8c:22:49"]
|
||||
|
||||
# Remove devices from provisioning
|
||||
# This is a legacy API call that removes the devices from Network Provisioning
|
||||
# in CVP versions older than 2021.3.0, however it does not remove them from
|
||||
# the Device Inventory as that requires the streaming agent (TerminAttr) to be shutdown,
|
||||
# which this API does not support.
|
||||
# To fully decommission a device the device_decommissioning() API can be used, which is
|
||||
# supported from 2021.3.0+.
|
||||
# Note that using the delete_devices() function post CVP 2021.3.0 the device will be
|
||||
# automatically added back to the Undefined container.
|
||||
clnt.api.delete_devices(devices)
|
|
@ -0,0 +1,23 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision
|
||||
clnt = CvpClient()
|
||||
clnt.connect(nodes=['cvp1'], username="username",password="password")
|
||||
|
||||
configletName = 'cvprac_example2'
|
||||
|
||||
device_name = "tp-avd-leaf1"
|
||||
device = clnt.api.get_device_by_name(device_name)
|
||||
|
||||
configlet = clnt.api.get_configlet_by_name(configletName)
|
||||
|
||||
clnt.api.apply_configlets_to_device("", device, [configlet])
|
20
docs/labs/lab03-configlet-management/backup_configlets.py
Normal file
20
docs/labs/lab03-configlet-management/backup_configlets.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
#
|
||||
# Get configlets and save them to individual files
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision
|
||||
clnt = CvpClient()
|
||||
clnt.connect(['cvp1'],'username', 'password')
|
||||
|
||||
configlets = clnt.api.get_configlets(start=0,end=0)
|
||||
|
||||
for configlet in configlets['data']:
|
||||
with open(configlet['name'],'w') as f:
|
||||
f.write(configlet['config'])
|
48
docs/labs/lab03-configlet-management/backup_configletsV2.py
Normal file
48
docs/labs/lab03-configlet-management/backup_configletsV2.py
Normal file
|
@ -0,0 +1,48 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
#
|
||||
# Get configlets and save them to individual files using multi threading
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from functools import wraps
|
||||
|
||||
# Create connection to CloudVision
|
||||
clnt = CvpClient()
|
||||
clnt.connect(['cvp1'],'username', 'password')
|
||||
|
||||
total = clnt.api.get_configlets(start=0,end=1)['total']
|
||||
|
||||
def get_list_of_configlets():
|
||||
"""
|
||||
Create a thread pool and download specified urls
|
||||
"""
|
||||
|
||||
futures_list = []
|
||||
results = []
|
||||
|
||||
with ThreadPoolExecutor(max_workers=40) as executor:
|
||||
for i in range(0,total+1,10):
|
||||
futures = executor.submit(clnt.api.get_configlets, start=i,end=i+10)
|
||||
futures_list.append(futures)
|
||||
|
||||
for future in futures_list:
|
||||
try:
|
||||
result = future.result(timeout=60)
|
||||
results.append(result)
|
||||
except Exception:
|
||||
results.append(None)
|
||||
print(future.result())
|
||||
return results
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
results = get_list_of_configlets()
|
||||
for future in results:
|
||||
for configlet in future['data']:
|
||||
with open(configlet['name'],'w') as f:
|
||||
f.write(configlet['config'])
|
6
docs/labs/lab03-configlet-management/common.cfg
Normal file
6
docs/labs/lab03-configlet-management/common.cfg
Normal file
|
@ -0,0 +1,6 @@
|
|||
!
|
||||
ip name-server vrf management 1.1.1.1
|
||||
ip name-server vrf management 8.8.8.8
|
||||
!
|
||||
ntp server vrf management time.google.com
|
||||
!
|
54
docs/labs/lab03-configlet-management/config_search.py
Normal file
54
docs/labs/lab03-configlet-management/config_search.py
Normal file
|
@ -0,0 +1,54 @@
|
|||
# Copyright (c) 2022 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision
|
||||
clnt = CvpClient()
|
||||
clnt.connect(nodes=['cvp1'], username="username",password="password")
|
||||
|
||||
def main():
|
||||
|
||||
print('Retrieving configlets ...')
|
||||
|
||||
inventory = clnt.api.get_inventory()
|
||||
data = clnt.api.get_configlets_and_mappers()['data']
|
||||
print(data)
|
||||
|
||||
print('Number of configlets:', len(data['configlets']))
|
||||
|
||||
searchAgain = True
|
||||
while searchAgain:
|
||||
try:
|
||||
search = input( "\nEnter Config Line: " )
|
||||
print(f"\n\n\'{search}\' has been found in following configlets:\n\n")
|
||||
print(f"{'Hostname':<30}{'Serial number':<50}{'MAC address':<30}{'Configlets':<40}")
|
||||
print("=" * 150)
|
||||
for i in inventory:
|
||||
device = i['hostname']
|
||||
device_sn = i['serialNumber']
|
||||
device_mac = i['systemMacAddress']
|
||||
configlet_list = []
|
||||
for c in data['configlets']:
|
||||
for g in data['generatedConfigletMappers']:
|
||||
if device_mac == g['netElementId'] and c['key'] == g['configletBuilderId'] and search in c['config']:
|
||||
configlet_list.append(c['name'])
|
||||
for k in data['configletMappers']:
|
||||
if device_mac == k['objectId'] and c['key'] == k['configletId'] and search in c['config']:
|
||||
configlet_list.append(c['name'])
|
||||
configlet_list_final = ",".join(configlet_list)
|
||||
if len(configlet_list) > 0:
|
||||
print(f"{device:<30}{device_sn:<50}{device_mac:<30}{configlet_list_final:<30}")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print('\nExiting... \n')
|
||||
return
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
6
docs/labs/lab03-configlet-management/configlet_list.txt
Normal file
6
docs/labs/lab03-configlet-management/configlet_list.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
tp-avd_tp-avd-leaf1
|
||||
tp-avd_tp-avd-leaf2
|
||||
tp-avd_tp-avd-leaf3
|
||||
tp-avd_tp-avd-leaf4
|
||||
tp-avd_tp-avd-spine1
|
||||
tp-avd_tp-avd-spine2
|
24
docs/labs/lab03-configlet-management/create_configlet.py
Normal file
24
docs/labs/lab03-configlet-management/create_configlet.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision
|
||||
clnt = CvpClient()
|
||||
clnt.connect(nodes=['cvp1'], username="username",password="password")
|
||||
|
||||
configletName = "cvprac_example"
|
||||
|
||||
configlet = """!
|
||||
interface Ethernet10
|
||||
description test
|
||||
ip address 10.144.144.1/24
|
||||
!
|
||||
"""
|
||||
|
||||
clnt.api.add_configlet(configletName,configlet)
|
|
@ -0,0 +1,19 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision
|
||||
clnt = CvpClient()
|
||||
clnt.connect(nodes=['cvp1'], username="username",password="password")
|
||||
|
||||
configletName = "cvprac_example2"
|
||||
|
||||
with open("common.cfg") as file:
|
||||
configlet = file.read()
|
||||
clnt.api.add_configlet(configletName, configlet)
|
|
@ -0,0 +1,36 @@
|
|||
# Copyright (c) 2023 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import ssl
|
||||
import argparse
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision
|
||||
clnt = CvpClient()
|
||||
clnt.connect(nodes=['cvp1'], username="username", password="password")
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Get the list of devices and containers a configlet is attached to')
|
||||
parser.add_argument('-c', '--configlet', required=True, help='The name of the configlet')
|
||||
args = parser.parse_args()
|
||||
|
||||
configlet_name = args.configlet
|
||||
devices = clnt.api.get_applied_devices(configlet_name)
|
||||
|
||||
containers = clnt.api.get_applied_containers(configlet_name)
|
||||
print(f"Total number of devices {configlet_name} is attached to: {devices['total']}\n")
|
||||
print(f"Total number of containers {configlet_name} is attached to: {containers['total']}\n")
|
||||
col1 = "Device FQDN/hostname"
|
||||
col2 = "IP Address"
|
||||
print(f"{col1:<40}{col2:<40}")
|
||||
print("="*80)
|
||||
for device in devices['data']:
|
||||
print(f"{device['hostName']:<40}{device['ipAddress']}")
|
||||
|
||||
print("\nList of containers:\n")
|
||||
for container in containers['data']:
|
||||
print(container['containerName'])
|
53
docs/labs/lab03-configlet-management/get_configlets.py
Normal file
53
docs/labs/lab03-configlet-management/get_configlets.py
Normal file
|
@ -0,0 +1,53 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
#
|
||||
# Get list of configlets in parallel
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import ssl
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision
|
||||
clnt = CvpClient()
|
||||
|
||||
clnt.connect(nodes=['cvp1'], username="username",password="password")
|
||||
|
||||
import time
|
||||
from functools import wraps
|
||||
|
||||
def get_list_of_configlets(configlets):
|
||||
"""
|
||||
Create a thread pool and download specified urls
|
||||
"""
|
||||
|
||||
futures_list = []
|
||||
results = []
|
||||
|
||||
with ThreadPoolExecutor(max_workers=40) as executor:
|
||||
for configlet in configlets:
|
||||
futures = executor.submit(clnt.api.get_configlet_by_name, configlet)
|
||||
futures_list.append(futures)
|
||||
|
||||
for future in futures_list:
|
||||
try:
|
||||
result = future.result(timeout=60)
|
||||
results.append(result)
|
||||
except Exception:
|
||||
results.append(None)
|
||||
return results
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Example with pre-defined list
|
||||
configlets = ["tp-avd_tp-avd-leaf1","tp-avd_tp-avd-leaf2","tp-avd_tp-avd-leaf3","tp-avd_tp-avd-leaf4"]
|
||||
|
||||
# Example with loading list of configlets from a file
|
||||
# with open("configlet_list.txt") as f:
|
||||
# configlets = f.read().splitlines()
|
||||
|
||||
results = get_list_of_configlets(configlets)
|
||||
for result in results:
|
||||
print(result)
|
|
@ -0,0 +1,26 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision
|
||||
clnt = CvpClient()
|
||||
clnt.connect(nodes=['cvp1'], username="username",password="password")
|
||||
|
||||
configletNames = ['tp-avd_tp-avd-leaf1','vlan144','api_models']
|
||||
|
||||
device_name = "tp-avd-leaf1"
|
||||
device = clnt.api.get_device_by_name(device_name)
|
||||
|
||||
configlets = []
|
||||
|
||||
for name in configletNames:
|
||||
configlets.append(clnt.api.get_configlet_by_name(name))
|
||||
|
||||
# Apply configlets in the order specified in the list
|
||||
clnt.api.apply_configlets_to_device("", device, configlets, reorder_configlets=True)
|
28
docs/labs/lab03-configlet-management/update_configlet.py
Normal file
28
docs/labs/lab03-configlet-management/update_configlet.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision
|
||||
clnt = CvpClient()
|
||||
clnt.connect(nodes=['cvp1'], username="username",password="password")
|
||||
|
||||
# Modify existing configlet
|
||||
|
||||
configletName = "cvprac_example"
|
||||
|
||||
configlet = """!
|
||||
interface Ethernet10
|
||||
description DUB_R04
|
||||
ip address 10.144.144.2/24
|
||||
!
|
||||
"""
|
||||
|
||||
configletID = clnt.api.get_configlet_by_name(configletName)['key']
|
||||
|
||||
clnt.api.update_configlet( configlet, configletID, configletName)
|
|
@ -0,0 +1,21 @@
|
|||
# Copyright (c) 2020 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision
|
||||
clnt = CvpClient()
|
||||
clnt.connect(['cvp1'],'username', 'password')
|
||||
|
||||
image_name = "vEOS-4.26.0.1F"
|
||||
image = clnt.api.get_image_bundle_by_name(image_name)
|
||||
|
||||
container_name = "TP_FABRIC"
|
||||
container = clnt.api.get_container_by_name(container_name)
|
||||
|
||||
clnt.api.apply_image_to_container(image, container)
|
|
@ -0,0 +1,23 @@
|
|||
# Copyright (c) 2020 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision
|
||||
clnt = CvpClient()
|
||||
clnt.connect(nodes=['cvp1'], username="username",password="password")
|
||||
|
||||
container_name = "TP_LEAFS"
|
||||
|
||||
configletName = 'cvprac_example2'
|
||||
|
||||
container = clnt.api.get_container_by_name(container_name)
|
||||
|
||||
configlet = clnt.api.get_configlet_by_name(configletName)
|
||||
|
||||
clnt.api.apply_configlets_to_container("", container, [configlet])
|
20
docs/labs/lab04-container-management/create_container.py
Normal file
20
docs/labs/lab04-container-management/create_container.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Copyright (c) 2020 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision
|
||||
clnt = CvpClient()
|
||||
clnt.connect(['cvp1'],'username', 'password')
|
||||
|
||||
# Get parent container information
|
||||
parent = clnt.api.get_container_by_name("ContainerA")
|
||||
|
||||
# Create new container ContainerB under ContainerA
|
||||
|
||||
clnt.api.add_container("ContainerB",parent["name"],parent["key"])
|
|
@ -0,0 +1,21 @@
|
|||
# Copyright (c) 2020 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision
|
||||
clnt = CvpClient()
|
||||
clnt.connect(['cvp1'],'username', 'password')
|
||||
|
||||
image_name = "vEOS-4.26.0.1F"
|
||||
image = clnt.api.get_image_bundle_by_name(image_name)
|
||||
|
||||
container_name = "TP_FABRIC"
|
||||
container = clnt.api.get_container_by_name(container_name)
|
||||
|
||||
clnt.api.remove_image_from_container(image, container)
|
32
docs/labs/lab04-container-management/rename_container.py
Normal file
32
docs/labs/lab04-container-management/rename_container.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
from cvprac.cvp_client import CvpClient
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision
|
||||
clnt = CvpClient()
|
||||
clnt.connect(['cvp1'],'username', 'password')
|
||||
oldName = "test"
|
||||
newName = "test121"
|
||||
|
||||
container_id = clnt.api.get_container_by_name(oldName)['key']
|
||||
|
||||
data = {"data":[{"info": "Container {} renamed from {}".format(newName, oldName),
|
||||
"infoPreview": "Container {} renamed from {}".format(newName, oldName),
|
||||
"action": "update",
|
||||
"nodeType": "container",
|
||||
"nodeId": container_id,
|
||||
"toId":"",
|
||||
"fromId":"",
|
||||
"nodeName": newName,
|
||||
"fromName": "",
|
||||
"toName": "",
|
||||
"toIdType": "container",
|
||||
"oldNodeName": oldName
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
clnt.api._add_temp_action(data)
|
||||
clnt.api._save_topology_v2([])
|
21
docs/labs/lab05-device-management/add_image_to_devices.py
Normal file
21
docs/labs/lab05-device-management/add_image_to_devices.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision
|
||||
clnt = CvpClient()
|
||||
clnt.connect(['cvp1'],'username', 'password')
|
||||
|
||||
image_name = "vEOS-4.26.0.1F"
|
||||
image = clnt.api.get_image_bundle_by_name(image_name)
|
||||
|
||||
device_name = "tp-avd-leaf2"
|
||||
device = clnt.api.get_device_by_name(device_name)
|
||||
|
||||
clnt.api.apply_image_to_device(image, device)
|
100
docs/labs/lab05-device-management/add_image_wo_tempaction.py
Normal file
100
docs/labs/lab05-device-management/add_image_wo_tempaction.py
Normal file
|
@ -0,0 +1,100 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import json
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision
|
||||
clnt = CvpClient()
|
||||
clnt.connect(['cvp1'],'username', 'password')
|
||||
|
||||
image_name = "vEOS-4.26.0.1F"
|
||||
image = clnt.api.get_image_bundle_by_name(image_name)
|
||||
|
||||
device_name = "tp-avd-leaf2"
|
||||
device = clnt.api.get_device_by_name(device_name)
|
||||
|
||||
def apply_image_to_element_no_temp(image, element, name, id_type, create_task=True):
|
||||
''' Apply an image bundle to a device or container
|
||||
A copy of the appl_image_to_element() function without creating a tempAction.
|
||||
Useful in situations where we need to call saveTopology on a per tempAction basis,
|
||||
which is only possible if the addTempAction function is not used and the data
|
||||
that we would've passed in the addTempAction call is passed in the
|
||||
saveTopology call.
|
||||
Args:
|
||||
image (dict): The image info.
|
||||
element (dict): Info about the element to apply an image to. Dict
|
||||
can contain device info or container info.
|
||||
name (str): Name of the element the image is being applied to.
|
||||
id_type (str): - Id type of the element the image is being applied to
|
||||
- can be 'netelement' or 'container'
|
||||
create_task (bool): Determines whether or not to execute a save
|
||||
and create the tasks (if any)
|
||||
Returns:
|
||||
response (list): A list that contains the tempAction data
|
||||
Ex: [{'NetworkRollbackTask': False,
|
||||
'taskJson': '[{
|
||||
"info": "Apply image: vEOS-4.26.0.1F to netelement tp-avd-leaf2",
|
||||
"infoPreview": "Apply image: vEOS-4.26.0.1F to netelement tp-avd-leaf2",
|
||||
"note": "",
|
||||
"action": "associate", "nodeType":
|
||||
"imagebundle",
|
||||
"nodeId": "imagebundle_1622072231719691917",
|
||||
"toId": "50:08:00:b1:5b:0b",
|
||||
"toIdType": "netelement",
|
||||
"fromId": "",
|
||||
"nodeName": "vEOS-4.26.0.1F",
|
||||
"fromName": "", "
|
||||
toName": "tp-avd-leaf2",
|
||||
"childTasks": [],
|
||||
"parentTask": ""}]'}]
|
||||
'''
|
||||
|
||||
print('Attempt to apply %s to %s %s' % (image['name'],
|
||||
id_type, name))
|
||||
info = 'Apply image: %s to %s %s' % (image['name'], id_type, name)
|
||||
node_id = ''
|
||||
if 'imageBundleKeys' in image:
|
||||
if image['imageBundleKeys']:
|
||||
node_id = image['imageBundleKeys'][0]
|
||||
print('Provided image is an image object.'
|
||||
' Using first value from imageBundleKeys - %s'
|
||||
% node_id)
|
||||
if 'id' in image:
|
||||
node_id = image['id']
|
||||
print('Provided image is an image bundle object.'
|
||||
' Found v1 API id field - %s' % node_id)
|
||||
elif 'key' in image:
|
||||
node_id = image['key']
|
||||
print('Provided image is an image bundle object.'
|
||||
' Found v2 API key field - %s' % node_id)
|
||||
data = [
|
||||
{
|
||||
"NetworkRollbackTask": False,
|
||||
"taskJson": json.dumps([{'info': info,
|
||||
'infoPreview': info,
|
||||
'note': '',
|
||||
'action': 'associate',
|
||||
'nodeType': 'imagebundle',
|
||||
'nodeId': node_id,
|
||||
'toId': element['key'],
|
||||
'toIdType': id_type,
|
||||
'fromId': '',
|
||||
'nodeName': image['name'],
|
||||
'fromName': '',
|
||||
'toName': name,
|
||||
'childTasks': [],
|
||||
'parentTask': ''}])
|
||||
}
|
||||
]
|
||||
return data
|
||||
|
||||
create_task = False
|
||||
tempAction = apply_image_to_element_no_temp(image, device, device['fqdn'], 'netelement', create_task)
|
||||
|
||||
clnt.api._save_topology_v2(tempAction)
|
|
@ -0,0 +1,21 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision
|
||||
clnt = CvpClient()
|
||||
clnt.connect(['cvp1'],'username', 'password')
|
||||
|
||||
image_name = "vEOS-4.26.0.1F"
|
||||
image = clnt.api.get_image_bundle_by_name(image_name)
|
||||
|
||||
device_name = "tp-avd-leaf2"
|
||||
device = clnt.api.get_device_by_name(device_name)
|
||||
|
||||
clnt.api.remove_image_from_device(image, device)
|
34
docs/labs/lab05-device-management/set_mgmt_ip.py
Normal file
34
docs/labs/lab05-device-management/set_mgmt_ip.py
Normal file
|
@ -0,0 +1,34 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision
|
||||
clnt = CvpClient()
|
||||
clnt.connect(['cvp1'],'username', 'password')
|
||||
|
||||
|
||||
data = {"data":[{"info":"Device IP Address Changed",
|
||||
"infoPreview":"<b> Device IP Address Changed to 10.83.13.214</b>",
|
||||
"action":"associate",
|
||||
"nodeType":"ipaddress",
|
||||
"nodeId":"",
|
||||
"toId":"50:08:00:a7:ca:c3", # MAC of the device
|
||||
"fromId":"",
|
||||
"nodeName":"",
|
||||
"fromName":"",
|
||||
"toName":"tp-avd-leaf1", # hostname
|
||||
"toIdType":"netelement",
|
||||
"nodeIpAddress":"10.83.13.219", # the temporary management IP Address
|
||||
"nodeTargetIpAddress":"10.83.13.214" # the final management IP address
|
||||
}
|
||||
]
|
||||
}
|
||||
clnt.api._add_temp_action(data)
|
||||
|
||||
clnt.api._save_topology_v2([])
|
120
docs/labs/lab06-provisioning/atd_e2e_provisioning_workflow.py
Normal file
120
docs/labs/lab06-provisioning/atd_e2e_provisioning_workflow.py
Normal file
|
@ -0,0 +1,120 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
# This script is an example on provisioning registered devices in CloudVision that is based on
|
||||
# Arista Test Drive (ATD) and similar to what the ansible playbooks do in
|
||||
# https://github.com/arista-netdevops-community/atd-avd.
|
||||
# It does the following:
|
||||
# - creates and uploads configlets,
|
||||
# - creates the container hierarchy in Network Provisiong
|
||||
# - moves the devices to their target containers
|
||||
# - assigns the configlets to the devices
|
||||
# - creates a change control from the genereated tasks
|
||||
# - approves and executes the change control
|
||||
|
||||
import uuid
|
||||
import time
|
||||
import ssl
|
||||
from datetime import datetime
|
||||
from cvprac.cvp_client import CvpClient
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
|
||||
# Create connection to CloudVision
|
||||
clnt = CvpClient()
|
||||
clnt.connect(['cvp1'],'username', 'password')
|
||||
|
||||
# Create container topology
|
||||
container_name = "DC1_LEAFS"
|
||||
container_topology = [{"containerName": "ATD_FABRIC", "parentContainerName": 'Tenant'},
|
||||
{"containerName": "ATD_LEAFS", "parentContainerName": 'ATD_FABRIC'},
|
||||
{"containerName": "pod1", "parentContainerName": 'ATD_LEAFS'},
|
||||
{"containerName": "pod2", "parentContainerName": 'ATD_LEAFS'},
|
||||
{"containerName": "ATD_SERVERS", "parentContainerName": 'ATD_FABRIC'},
|
||||
{"containerName": "ATD_SPINES", "parentContainerName": 'ATD_FABRIC'},
|
||||
{"containerName": "ATD_TENANT_NETWORKS", "parentContainerName": 'ATD_FABRIC'}]
|
||||
for container in container_topology:
|
||||
try:
|
||||
container_name = container['containerName']
|
||||
# Get parent container information
|
||||
parent = clnt.api.get_container_by_name(container['parentContainerName'])
|
||||
print(f'Creating container {container_name}\n')
|
||||
clnt.api.add_container(container_name,parent["name"],parent["key"])
|
||||
except Exception as e:
|
||||
if "Data already exists in Database" in str(e):
|
||||
print ("Container already exists, continuing...")
|
||||
|
||||
# Create device mappers
|
||||
devices = [{'deviceName': "leaf1",
|
||||
'configlets': ["BaseIPv4_Leaf1", "AVD_leaf1"],
|
||||
"parentContainerName": "pod1"},
|
||||
{'deviceName': "leaf2",
|
||||
'configlets': ["BaseIPv4_Leaf2", "AVD_leaf2"],
|
||||
"parentContainerName": "pod1"},
|
||||
{'deviceName': "leaf3",
|
||||
'configlets': ["BaseIPv4_Leaf3", "AVD_leaf3"],
|
||||
"parentContainerName": "pod2"},
|
||||
{'deviceName': "leaf4",
|
||||
'configlets': ["BaseIPv4_Leaf4", "AVD_leaf4"],
|
||||
"parentContainerName": "pod2"},
|
||||
{'deviceName': "spine1",
|
||||
'configlets': ["BaseIPv4_Spine1", "AVD_spine1"],
|
||||
"parentContainerName": "ATD_SPINES"},
|
||||
{'deviceName': "spine2",
|
||||
'configlets': ["BaseIPv4_Spine2", "AVD_spine2"],
|
||||
"parentContainerName": "ATD_SPINES"}]
|
||||
|
||||
task_list = []
|
||||
for device in devices:
|
||||
# Load the AVD configlets from file
|
||||
with open("./configlets/AVD_" + device['deviceName'] + ".cfg", "r") as file:
|
||||
configlet_file = file.read()
|
||||
avd_configlet_name = device['configlets'][1]
|
||||
base_configlet_name = device['configlets'][0] # preloaded configlet in an ATD environment
|
||||
container_name = device['parentContainerName']
|
||||
base_configlet = clnt.api.get_configlet_by_name(base_configlet_name)
|
||||
configlets = [base_configlet]
|
||||
# Update the AVD configlets if they exist, otherwise upload them from the configlets folder
|
||||
print (f"Creating configlet {avd_configlet_name} for {device['deviceName']}\n")
|
||||
try:
|
||||
configlet = clnt.api.get_configlet_by_name(avd_configlet_name)
|
||||
clnt.api.update_configlet(configlet_file, configlet['key'], avd_configlet_name)
|
||||
configlets.append(configlet)
|
||||
except:
|
||||
clnt.api.add_configlet(avd_configlet_name, configlet_file)
|
||||
configlet = clnt.api.get_configlet_by_name(avd_configlet_name)
|
||||
configlets.append(configlet)
|
||||
# Get device data
|
||||
device_data = clnt.api.get_device_by_name(device['deviceName'] + ".atd.lab")
|
||||
# Get the parent container data for the device
|
||||
container = clnt.api.get_container_by_name(container_name)
|
||||
device_name = device['deviceName']
|
||||
print(f"Moving device {device_name} to container {container_name}\n")
|
||||
# The move action will create the task first, however if the devices are already in the target
|
||||
# container, for instance if the script was run multiple times than the move action will
|
||||
# not generate a task anymore, therefore it's better to create the task list from the
|
||||
# Update Config action which will reuse the Move Device action's task if one exists,
|
||||
# otherwise will create a new one.
|
||||
move = clnt.api.move_device_to_container("python", device_data, container)
|
||||
apply_configlets = clnt.api.apply_configlets_to_device("", device_data, configlets)
|
||||
task_list = task_list + apply_configlets['data']['taskIds']
|
||||
|
||||
print(f"Generated task IDs are: {task_list}\n")
|
||||
|
||||
# Generate unique ID for the change control
|
||||
cc_id = str(uuid.uuid4())
|
||||
cc_name = f"Change_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
||||
|
||||
print("Creating Change control with the list of tasks")
|
||||
clnt.api.change_control_create_for_tasks(cc_id, cc_name, task_list, series=False)
|
||||
|
||||
print("Approving Change Control")
|
||||
# adding a few seconds sleep to avoid small time diff between the local system and CVP
|
||||
time.sleep(2)
|
||||
approve_note = "Approving CC via cvprac"
|
||||
clnt.api.change_control_approve(cc_id, notes=approve_note)
|
||||
|
||||
# Start the change control
|
||||
print("Executing Change Control...")
|
||||
start_note = "Start the CC via cvprac"
|
||||
clnt.api.change_control_start(cc_id, notes=start_note)
|
64
docs/labs/lab06-provisioning/auto_reconcile_on_rc_change.py
Normal file
64
docs/labs/lab06-provisioning/auto_reconcile_on_rc_change.py
Normal file
|
@ -0,0 +1,64 @@
|
|||
# Copyright (c) 2022 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
# This script can be run as a cronjob to periodically reconcile all devices
|
||||
# that are out of configuration compliance in environments where the running-config
|
||||
# is still modified via the CLI often.
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import ssl
|
||||
from datetime import datetime
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
clnt = CvpClient()
|
||||
clnt.set_log_level(log_level='WARNING')
|
||||
|
||||
# Reading the service account token from a file
|
||||
with open("token.tok") as f:
|
||||
token = f.read().strip('\n')
|
||||
|
||||
clnt = CvpClient()
|
||||
clnt.connect(nodes=['cvp1'], username='',password='',api_token=token)
|
||||
|
||||
inventory = clnt.api.get_inventory()
|
||||
|
||||
compliance = {"0001": "Config is out of sync",
|
||||
"0003": "Config & image out of sync",
|
||||
"0004": "Config, Image and Device time are in sync",
|
||||
"0005": "Device is not reachable",
|
||||
"0008": "Config, Image and Extensions are out of sync",
|
||||
"0009": "Config and Extensions are out of sync",
|
||||
"0012": "Config, Image, Extension and Device time are out of sync",
|
||||
"0013": "Config, Image and Device time are out of sync",
|
||||
"0014": "Config, Extensions and Device time are out of sync",
|
||||
"0016": "Config and Device time are out of sync"
|
||||
}
|
||||
|
||||
non_compliants = []
|
||||
taskIds = []
|
||||
for device in inventory:
|
||||
if device['complianceCode'] in compliance.keys():
|
||||
# create a list of non-compliant devices for reporting purposes
|
||||
non_compliants.append(device['hostname'])
|
||||
dev_mac = device['systemMacAddress']
|
||||
# check if device already has reconciled config and save the key if it does
|
||||
try:
|
||||
configlets = clnt.api.get_configlets_by_device_id(dev_mac)
|
||||
for configlet in configlets:
|
||||
if configlet['reconciled'] == True:
|
||||
configlet_key = configlet['key']
|
||||
break
|
||||
else:
|
||||
configlet_key = ""
|
||||
rc = clnt.api.get_device_configuration(dev_mac)
|
||||
name = 'RECONCILE_' + device['serialNumber']
|
||||
update = clnt.api.update_reconcile_configlet(dev_mac, rc, configlet_key, name, True)
|
||||
# if the device had no reconciled config, it means we need to append the reconciled
|
||||
# configlet to the list of applied configlets on the device
|
||||
if configlet_key == "":
|
||||
addcfg = clnt.api.apply_configlets_to_device("auto-reconciling",device,[update['data']])
|
||||
clnt.api.cancel_task(addcfg['data']['taskIds'][0])
|
||||
except Exception as e:
|
||||
continue
|
||||
print(f"The non compliant devices were: {str(non_compliants)}")
|
81
docs/labs/lab06-provisioning/change_control_custom_rapi.py
Normal file
81
docs/labs/lab06-provisioning/change_control_custom_rapi.py
Normal file
|
@ -0,0 +1,81 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
#
|
||||
# NOTE: The following example is using the new Change Control Resource APIs supported in 2021.2.0 or newer and in CVaaS.
|
||||
# For CVaaS service-account token based auth has to be used.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import ssl
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision
|
||||
clnt = CvpClient()
|
||||
clnt.connect(['cvp1'],'username', 'password')
|
||||
|
||||
|
||||
cc_id = str(uuid.uuid4())
|
||||
name = f"Change_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
||||
|
||||
# Create custom stage hierarchy
|
||||
# The below example would result in the following hierarchy:
|
||||
# root (series)
|
||||
# |- stages 1-2 (series)
|
||||
# | |- stage 1ab (parallel)
|
||||
# | | |- stage 1a
|
||||
# | | |- stage 1b
|
||||
# | |- stage 2
|
||||
# |- stage 3
|
||||
data = {'key': {
|
||||
'id': cc_id
|
||||
},
|
||||
'change': {
|
||||
'name': cc_id,
|
||||
'notes': 'cvprac CC',
|
||||
'rootStageId': 'root',
|
||||
'stages': {'values': {'root': {'name': 'root',
|
||||
'rows': {'values': [{'values': ['1-2']},
|
||||
{'values': ['3']}]
|
||||
}
|
||||
},
|
||||
'1-2': {'name': 'stages 1-2',
|
||||
'rows': {'values': [{'values': ['1ab']},
|
||||
{'values': ['2']}]}},
|
||||
'1ab': {'name': 'stage 1ab',
|
||||
'rows': {'values': [{'values': ['1a','1b']}]
|
||||
}
|
||||
},
|
||||
'1a': {'action': {'args': {'values': {'TaskID': '1242'}},
|
||||
'name': 'task',
|
||||
'timeout': 3000},
|
||||
'name': 'stage 1a'},
|
||||
'1b': {'action': {'args': {'values': {'TaskID': '1243'}},
|
||||
'name': 'task',
|
||||
'timeout': 3000},
|
||||
'name': 'stage 1b'},
|
||||
'2': {'action': {'args': {'values': {'TaskID': '1240'}},
|
||||
'name': 'task',
|
||||
'timeout': 3000},
|
||||
'name': 'stage 2'},
|
||||
'3': {'action': {'args': {'values': {'TaskID': '1241'}},
|
||||
'name': 'task',
|
||||
'timeout': 3000},
|
||||
'name': 'stage 3'},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
# Create change control from custom stage hierarchy data
|
||||
clnt.api.change_control_create_with_custom_stages(data)
|
||||
|
||||
# Approve the change control
|
||||
approval_note = "Approve CC via cvprac" # notes are optional
|
||||
clnt.api.change_control_approve(cc_id, notes=approval_note)
|
||||
|
||||
# Start the change control
|
||||
start_note = "Starting CC via cvprac" # notes are optional
|
||||
clnt.api.change_control_start(cc_id, notes=start_note)
|
27
docs/labs/lab06-provisioning/change_control_workflow.py
Normal file
27
docs/labs/lab06-provisioning/change_control_workflow.py
Normal file
|
@ -0,0 +1,27 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
from datetime import datetime
|
||||
|
||||
# Note API token auth method is not yet supported with Change Controls
|
||||
clnt = CvpClient()
|
||||
clnt.connect(['cvp1'],'username', 'password')
|
||||
|
||||
ccid = 'cvprac0904211418'
|
||||
name = "cvprac CC test"
|
||||
tlist = ['1021','1020','1019','1018']
|
||||
|
||||
### Create Change control with the list of tasks
|
||||
clnt.api.create_change_control_v3(ccid, name, tlist)
|
||||
|
||||
### Approve CC
|
||||
clnt.api.approve_change_control('cvprac0904211418', timestamp=datetime.utcnow().isoformat() + 'Z')
|
||||
|
||||
### Execute CC
|
||||
clnt.api.execute_change_controls(['cvprac0904211418'])
|
40
docs/labs/lab06-provisioning/change_control_workflow_rapi.py
Normal file
40
docs/labs/lab06-provisioning/change_control_workflow_rapi.py
Normal file
|
@ -0,0 +1,40 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
#
|
||||
# NOTE: The following example is using the new Change Control Resource APIs supported in 2021.2.0 or newer and in CVaaS.
|
||||
# For CVaaS service-account token based auth has to be used.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import ssl
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision
|
||||
clnt = CvpClient()
|
||||
clnt.connect(['cvp1'],'username', 'password')
|
||||
|
||||
# Generate change control id and change control name
|
||||
cc_id = str(uuid.uuid4())
|
||||
name = f"Change_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
||||
|
||||
# Select the tasks and create a CC where all tasks will be run in parallel
|
||||
tasks = ["1249","1250","1251","1252"]
|
||||
clnt.api.change_control_create_for_tasks(cc_id, name, tasks, series=False)
|
||||
|
||||
# Approve the change control
|
||||
approve_note = "Approving CC via cvprac"
|
||||
clnt.api.change_control_approve(cc_id, notes=approve_note)
|
||||
|
||||
# # Schedule the change control
|
||||
# # Executing scheduled CCs might only work post 2021.3.0+
|
||||
# schedule_note = "Scheduling CC via cvprac"
|
||||
# schedule_time = "2021-12-23T03:17:00Z"
|
||||
# clnt.api.change_control_schedule(cc_id,schedule_time,notes=schedule_note)
|
||||
|
||||
# Start the change control
|
||||
start_note = "Start the CC via cvprac"
|
||||
clnt.api.change_control_start(cc_id, notes=start_note)
|
255
docs/labs/lab06-provisioning/configlets/AVD_leaf1.cfg
Normal file
255
docs/labs/lab06-provisioning/configlets/AVD_leaf1.cfg
Normal file
|
@ -0,0 +1,255 @@
|
|||
!RANCID-CONTENT-TYPE: arista
|
||||
!
|
||||
vlan internal order ascending range 1006 1199
|
||||
!
|
||||
transceiver qsfp default-mode 4x10G
|
||||
!
|
||||
service routing protocols model multi-agent
|
||||
!
|
||||
hostname leaf1
|
||||
ip name-server vrf default 8.8.8.8
|
||||
ip name-server vrf default 192.168.2.1
|
||||
dns domain atd.lab
|
||||
!
|
||||
spanning-tree mode mstp
|
||||
no spanning-tree vlan-id 4093-4094
|
||||
spanning-tree mst 0 priority 16384
|
||||
!
|
||||
no enable password
|
||||
no aaa root
|
||||
!
|
||||
vlan 110
|
||||
name Tenant_A_OP_Zone_1
|
||||
!
|
||||
vlan 160
|
||||
name Tenant_A_VMOTION
|
||||
!
|
||||
vlan 3009
|
||||
name MLAG_iBGP_Tenant_A_OP_Zone
|
||||
trunk group LEAF_PEER_L3
|
||||
!
|
||||
vlan 4093
|
||||
name LEAF_PEER_L3
|
||||
trunk group LEAF_PEER_L3
|
||||
!
|
||||
vlan 4094
|
||||
name MLAG_PEER
|
||||
trunk group MLAG
|
||||
!
|
||||
vrf instance Tenant_A_OP_Zone
|
||||
!
|
||||
interface Port-Channel1
|
||||
description MLAG_PEER_leaf2_Po1
|
||||
no shutdown
|
||||
switchport
|
||||
switchport trunk allowed vlan 2-4094
|
||||
switchport mode trunk
|
||||
switchport trunk group LEAF_PEER_L3
|
||||
switchport trunk group MLAG
|
||||
!
|
||||
interface Port-Channel4
|
||||
description host1_PortChannel
|
||||
no shutdown
|
||||
switchport
|
||||
switchport access vlan 110
|
||||
mlag 4
|
||||
!
|
||||
interface Ethernet1
|
||||
description MLAG_PEER_leaf2_Ethernet1
|
||||
no shutdown
|
||||
channel-group 1 mode active
|
||||
!
|
||||
interface Ethernet2
|
||||
description P2P_LINK_TO_SPINE1_Ethernet2
|
||||
no shutdown
|
||||
mtu 1500
|
||||
no switchport
|
||||
ip address 172.30.255.1/31
|
||||
!
|
||||
interface Ethernet3
|
||||
description P2P_LINK_TO_SPINE2_Ethernet2
|
||||
no shutdown
|
||||
mtu 1500
|
||||
no switchport
|
||||
ip address 172.30.255.3/31
|
||||
!
|
||||
interface Ethernet4
|
||||
description host1_Eth1
|
||||
no shutdown
|
||||
channel-group 4 mode active
|
||||
!
|
||||
interface Ethernet5
|
||||
description host1_Eth2
|
||||
no shutdown
|
||||
channel-group 4 mode active
|
||||
!
|
||||
interface Ethernet6
|
||||
description MLAG_PEER_leaf2_Ethernet6
|
||||
no shutdown
|
||||
channel-group 1 mode active
|
||||
!
|
||||
interface Loopback0
|
||||
description EVPN_Overlay_Peering
|
||||
no shutdown
|
||||
ip address 192.0.255.3/32
|
||||
!
|
||||
interface Loopback1
|
||||
description VTEP_VXLAN_Tunnel_Source
|
||||
no shutdown
|
||||
ip address 192.0.254.3/32
|
||||
!
|
||||
interface Loopback100
|
||||
description Tenant_A_OP_Zone_VTEP_DIAGNOSTICS
|
||||
no shutdown
|
||||
vrf Tenant_A_OP_Zone
|
||||
ip address 10.255.1.3/32
|
||||
!
|
||||
interface Management1
|
||||
description oob_management
|
||||
no shutdown
|
||||
ip address 192.168.0.12/24
|
||||
!
|
||||
interface Vlan110
|
||||
description Tenant_A_OP_Zone_1
|
||||
no shutdown
|
||||
vrf Tenant_A_OP_Zone
|
||||
ip address virtual 10.1.10.1/24
|
||||
!
|
||||
interface Vlan3009
|
||||
description MLAG_PEER_L3_iBGP: vrf Tenant_A_OP_Zone
|
||||
no shutdown
|
||||
mtu 1500
|
||||
vrf Tenant_A_OP_Zone
|
||||
ip address 10.255.251.0/31
|
||||
!
|
||||
interface Vlan4093
|
||||
description MLAG_PEER_L3_PEERING
|
||||
no shutdown
|
||||
mtu 1500
|
||||
ip address 10.255.251.0/31
|
||||
!
|
||||
interface Vlan4094
|
||||
description MLAG_PEER
|
||||
no shutdown
|
||||
mtu 1500
|
||||
no autostate
|
||||
ip address 10.255.252.0/31
|
||||
!
|
||||
interface Vxlan1
|
||||
description leaf1_VTEP
|
||||
vxlan source-interface Loopback1
|
||||
vxlan virtual-router encapsulation mac-address mlag-system-id
|
||||
vxlan udp-port 4789
|
||||
vxlan vlan 110 vni 10110
|
||||
vxlan vlan 160 vni 55160
|
||||
vxlan vrf Tenant_A_OP_Zone vni 10
|
||||
!
|
||||
ip virtual-router mac-address 00:1c:73:00:dc:01
|
||||
!
|
||||
ip address virtual source-nat vrf Tenant_A_OP_Zone address 10.255.1.3
|
||||
!
|
||||
ip routing
|
||||
ip routing vrf Tenant_A_OP_Zone
|
||||
!
|
||||
ip prefix-list PL-LOOPBACKS-EVPN-OVERLAY
|
||||
seq 10 permit 192.0.255.0/24 eq 32
|
||||
seq 20 permit 192.0.254.0/24 eq 32
|
||||
!
|
||||
mlag configuration
|
||||
domain-id pod1
|
||||
local-interface Vlan4094
|
||||
peer-address 10.255.252.1
|
||||
peer-link Port-Channel1
|
||||
reload-delay mlag 300
|
||||
reload-delay non-mlag 330
|
||||
!
|
||||
ip route 0.0.0.0/0 192.168.0.1
|
||||
!
|
||||
route-map RM-CONN-2-BGP permit 10
|
||||
match ip address prefix-list PL-LOOPBACKS-EVPN-OVERLAY
|
||||
!
|
||||
route-map RM-MLAG-PEER-IN permit 10
|
||||
description Make routes learned over MLAG Peer-link less preferred on spines to ensure optimal routing
|
||||
set origin incomplete
|
||||
!
|
||||
router bfd
|
||||
multihop interval 1200 min-rx 1200 multiplier 3
|
||||
!
|
||||
router bgp 65101
|
||||
router-id 192.0.255.3
|
||||
no bgp default ipv4-unicast
|
||||
distance bgp 20 200 200
|
||||
graceful-restart restart-time 300
|
||||
graceful-restart
|
||||
maximum-paths 4 ecmp 4
|
||||
neighbor EVPN-OVERLAY-PEERS peer group
|
||||
neighbor EVPN-OVERLAY-PEERS update-source Loopback0
|
||||
neighbor EVPN-OVERLAY-PEERS bfd
|
||||
neighbor EVPN-OVERLAY-PEERS ebgp-multihop 3
|
||||
neighbor EVPN-OVERLAY-PEERS password 7 q+VNViP5i4rVjW1cxFv2wA==
|
||||
neighbor EVPN-OVERLAY-PEERS send-community
|
||||
neighbor EVPN-OVERLAY-PEERS maximum-routes 0
|
||||
neighbor IPv4-UNDERLAY-PEERS peer group
|
||||
neighbor IPv4-UNDERLAY-PEERS password 7 AQQvKeimxJu+uGQ/yYvv9w==
|
||||
neighbor IPv4-UNDERLAY-PEERS send-community
|
||||
neighbor IPv4-UNDERLAY-PEERS maximum-routes 12000
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER peer group
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER remote-as 65101
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER next-hop-self
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER description leaf2
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER password 7 vnEaG8gMeQf3d3cN6PktXQ==
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER send-community
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER maximum-routes 12000
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER route-map RM-MLAG-PEER-IN in
|
||||
neighbor 10.255.251.1 peer group MLAG-IPv4-UNDERLAY-PEER
|
||||
neighbor 10.255.251.1 description leaf2
|
||||
neighbor 172.30.255.0 peer group IPv4-UNDERLAY-PEERS
|
||||
neighbor 172.30.255.0 remote-as 65001
|
||||
neighbor 172.30.255.0 description spine1_Ethernet2
|
||||
neighbor 172.30.255.2 peer group IPv4-UNDERLAY-PEERS
|
||||
neighbor 172.30.255.2 remote-as 65001
|
||||
neighbor 172.30.255.2 description spine2_Ethernet2
|
||||
neighbor 192.0.255.1 peer group EVPN-OVERLAY-PEERS
|
||||
neighbor 192.0.255.1 remote-as 65001
|
||||
neighbor 192.0.255.1 description spine1
|
||||
neighbor 192.0.255.2 peer group EVPN-OVERLAY-PEERS
|
||||
neighbor 192.0.255.2 remote-as 65001
|
||||
neighbor 192.0.255.2 description spine2
|
||||
redistribute connected route-map RM-CONN-2-BGP
|
||||
!
|
||||
vlan-aware-bundle Tenant_A_OP_Zone
|
||||
rd 192.0.255.3:10
|
||||
route-target both 10:10
|
||||
redistribute learned
|
||||
vlan 110
|
||||
!
|
||||
vlan-aware-bundle Tenant_A_VMOTION
|
||||
rd 192.0.255.3:55160
|
||||
route-target both 55160:55160
|
||||
redistribute learned
|
||||
vlan 160
|
||||
!
|
||||
address-family evpn
|
||||
neighbor EVPN-OVERLAY-PEERS activate
|
||||
!
|
||||
address-family ipv4
|
||||
no neighbor EVPN-OVERLAY-PEERS activate
|
||||
neighbor IPv4-UNDERLAY-PEERS activate
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER activate
|
||||
!
|
||||
vrf Tenant_A_OP_Zone
|
||||
rd 192.0.255.3:10
|
||||
route-target import evpn 10:10
|
||||
route-target export evpn 10:10
|
||||
router-id 192.0.255.3
|
||||
neighbor 10.255.251.1 peer group MLAG-IPv4-UNDERLAY-PEER
|
||||
redistribute connected
|
||||
!
|
||||
management api http-commands
|
||||
protocol https
|
||||
no shutdown
|
||||
!
|
||||
vrf default
|
||||
no shutdown
|
||||
!
|
||||
end
|
255
docs/labs/lab06-provisioning/configlets/AVD_leaf2.cfg
Normal file
255
docs/labs/lab06-provisioning/configlets/AVD_leaf2.cfg
Normal file
|
@ -0,0 +1,255 @@
|
|||
!RANCID-CONTENT-TYPE: arista
|
||||
!
|
||||
vlan internal order ascending range 1006 1199
|
||||
!
|
||||
transceiver qsfp default-mode 4x10G
|
||||
!
|
||||
service routing protocols model multi-agent
|
||||
!
|
||||
hostname leaf2
|
||||
ip name-server vrf default 8.8.8.8
|
||||
ip name-server vrf default 192.168.2.1
|
||||
dns domain atd.lab
|
||||
!
|
||||
spanning-tree mode mstp
|
||||
no spanning-tree vlan-id 4093-4094
|
||||
spanning-tree mst 0 priority 16384
|
||||
!
|
||||
no enable password
|
||||
no aaa root
|
||||
!
|
||||
vlan 110
|
||||
name Tenant_A_OP_Zone_1
|
||||
!
|
||||
vlan 160
|
||||
name Tenant_A_VMOTION
|
||||
!
|
||||
vlan 3009
|
||||
name MLAG_iBGP_Tenant_A_OP_Zone
|
||||
trunk group LEAF_PEER_L3
|
||||
!
|
||||
vlan 4093
|
||||
name LEAF_PEER_L3
|
||||
trunk group LEAF_PEER_L3
|
||||
!
|
||||
vlan 4094
|
||||
name MLAG_PEER
|
||||
trunk group MLAG
|
||||
!
|
||||
vrf instance Tenant_A_OP_Zone
|
||||
!
|
||||
interface Port-Channel1
|
||||
description MLAG_PEER_leaf1_Po1
|
||||
no shutdown
|
||||
switchport
|
||||
switchport trunk allowed vlan 2-4094
|
||||
switchport mode trunk
|
||||
switchport trunk group LEAF_PEER_L3
|
||||
switchport trunk group MLAG
|
||||
!
|
||||
interface Port-Channel4
|
||||
description host1_PortChannel
|
||||
no shutdown
|
||||
switchport
|
||||
switchport access vlan 110
|
||||
mlag 4
|
||||
!
|
||||
interface Ethernet1
|
||||
description MLAG_PEER_leaf1_Ethernet1
|
||||
no shutdown
|
||||
channel-group 1 mode active
|
||||
!
|
||||
interface Ethernet2
|
||||
description P2P_LINK_TO_SPINE1_Ethernet3
|
||||
no shutdown
|
||||
mtu 1500
|
||||
no switchport
|
||||
ip address 172.30.255.5/31
|
||||
!
|
||||
interface Ethernet3
|
||||
description P2P_LINK_TO_SPINE2_Ethernet3
|
||||
no shutdown
|
||||
mtu 1500
|
||||
no switchport
|
||||
ip address 172.30.255.7/31
|
||||
!
|
||||
interface Ethernet4
|
||||
description host1_Eth3
|
||||
no shutdown
|
||||
channel-group 4 mode active
|
||||
!
|
||||
interface Ethernet5
|
||||
description host1_Eth4
|
||||
no shutdown
|
||||
channel-group 4 mode active
|
||||
!
|
||||
interface Ethernet6
|
||||
description MLAG_PEER_leaf1_Ethernet6
|
||||
no shutdown
|
||||
channel-group 1 mode active
|
||||
!
|
||||
interface Loopback0
|
||||
description EVPN_Overlay_Peering
|
||||
no shutdown
|
||||
ip address 192.0.255.4/32
|
||||
!
|
||||
interface Loopback1
|
||||
description VTEP_VXLAN_Tunnel_Source
|
||||
no shutdown
|
||||
ip address 192.0.254.3/32
|
||||
!
|
||||
interface Loopback100
|
||||
description Tenant_A_OP_Zone_VTEP_DIAGNOSTICS
|
||||
no shutdown
|
||||
vrf Tenant_A_OP_Zone
|
||||
ip address 10.255.1.4/32
|
||||
!
|
||||
interface Management1
|
||||
description oob_management
|
||||
no shutdown
|
||||
ip address 192.168.0.13/24
|
||||
!
|
||||
interface Vlan110
|
||||
description Tenant_A_OP_Zone_1
|
||||
no shutdown
|
||||
vrf Tenant_A_OP_Zone
|
||||
ip address virtual 10.1.10.1/24
|
||||
!
|
||||
interface Vlan3009
|
||||
description MLAG_PEER_L3_iBGP: vrf Tenant_A_OP_Zone
|
||||
no shutdown
|
||||
mtu 1500
|
||||
vrf Tenant_A_OP_Zone
|
||||
ip address 10.255.251.1/31
|
||||
!
|
||||
interface Vlan4093
|
||||
description MLAG_PEER_L3_PEERING
|
||||
no shutdown
|
||||
mtu 1500
|
||||
ip address 10.255.251.1/31
|
||||
!
|
||||
interface Vlan4094
|
||||
description MLAG_PEER
|
||||
no shutdown
|
||||
mtu 1500
|
||||
no autostate
|
||||
ip address 10.255.252.1/31
|
||||
!
|
||||
interface Vxlan1
|
||||
description leaf2_VTEP
|
||||
vxlan source-interface Loopback1
|
||||
vxlan virtual-router encapsulation mac-address mlag-system-id
|
||||
vxlan udp-port 4789
|
||||
vxlan vlan 110 vni 10110
|
||||
vxlan vlan 160 vni 55160
|
||||
vxlan vrf Tenant_A_OP_Zone vni 10
|
||||
!
|
||||
ip virtual-router mac-address 00:1c:73:00:dc:01
|
||||
!
|
||||
ip address virtual source-nat vrf Tenant_A_OP_Zone address 10.255.1.4
|
||||
!
|
||||
ip routing
|
||||
ip routing vrf Tenant_A_OP_Zone
|
||||
!
|
||||
ip prefix-list PL-LOOPBACKS-EVPN-OVERLAY
|
||||
seq 10 permit 192.0.255.0/24 eq 32
|
||||
seq 20 permit 192.0.254.0/24 eq 32
|
||||
!
|
||||
mlag configuration
|
||||
domain-id pod1
|
||||
local-interface Vlan4094
|
||||
peer-address 10.255.252.0
|
||||
peer-link Port-Channel1
|
||||
reload-delay mlag 300
|
||||
reload-delay non-mlag 330
|
||||
!
|
||||
ip route 0.0.0.0/0 192.168.0.1
|
||||
!
|
||||
route-map RM-CONN-2-BGP permit 10
|
||||
match ip address prefix-list PL-LOOPBACKS-EVPN-OVERLAY
|
||||
!
|
||||
route-map RM-MLAG-PEER-IN permit 10
|
||||
description Make routes learned over MLAG Peer-link less preferred on spines to ensure optimal routing
|
||||
set origin incomplete
|
||||
!
|
||||
router bfd
|
||||
multihop interval 1200 min-rx 1200 multiplier 3
|
||||
!
|
||||
router bgp 65101
|
||||
router-id 192.0.255.4
|
||||
no bgp default ipv4-unicast
|
||||
distance bgp 20 200 200
|
||||
graceful-restart restart-time 300
|
||||
graceful-restart
|
||||
maximum-paths 4 ecmp 4
|
||||
neighbor EVPN-OVERLAY-PEERS peer group
|
||||
neighbor EVPN-OVERLAY-PEERS update-source Loopback0
|
||||
neighbor EVPN-OVERLAY-PEERS bfd
|
||||
neighbor EVPN-OVERLAY-PEERS ebgp-multihop 3
|
||||
neighbor EVPN-OVERLAY-PEERS password 7 q+VNViP5i4rVjW1cxFv2wA==
|
||||
neighbor EVPN-OVERLAY-PEERS send-community
|
||||
neighbor EVPN-OVERLAY-PEERS maximum-routes 0
|
||||
neighbor IPv4-UNDERLAY-PEERS peer group
|
||||
neighbor IPv4-UNDERLAY-PEERS password 7 AQQvKeimxJu+uGQ/yYvv9w==
|
||||
neighbor IPv4-UNDERLAY-PEERS send-community
|
||||
neighbor IPv4-UNDERLAY-PEERS maximum-routes 12000
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER peer group
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER remote-as 65101
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER next-hop-self
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER description leaf1
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER password 7 vnEaG8gMeQf3d3cN6PktXQ==
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER send-community
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER maximum-routes 12000
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER route-map RM-MLAG-PEER-IN in
|
||||
neighbor 10.255.251.0 peer group MLAG-IPv4-UNDERLAY-PEER
|
||||
neighbor 10.255.251.0 description leaf1
|
||||
neighbor 172.30.255.4 peer group IPv4-UNDERLAY-PEERS
|
||||
neighbor 172.30.255.4 remote-as 65001
|
||||
neighbor 172.30.255.4 description spine1_Ethernet3
|
||||
neighbor 172.30.255.6 peer group IPv4-UNDERLAY-PEERS
|
||||
neighbor 172.30.255.6 remote-as 65001
|
||||
neighbor 172.30.255.6 description spine2_Ethernet3
|
||||
neighbor 192.0.255.1 peer group EVPN-OVERLAY-PEERS
|
||||
neighbor 192.0.255.1 remote-as 65001
|
||||
neighbor 192.0.255.1 description spine1
|
||||
neighbor 192.0.255.2 peer group EVPN-OVERLAY-PEERS
|
||||
neighbor 192.0.255.2 remote-as 65001
|
||||
neighbor 192.0.255.2 description spine2
|
||||
redistribute connected route-map RM-CONN-2-BGP
|
||||
!
|
||||
vlan-aware-bundle Tenant_A_OP_Zone
|
||||
rd 192.0.255.4:10
|
||||
route-target both 10:10
|
||||
redistribute learned
|
||||
vlan 110
|
||||
!
|
||||
vlan-aware-bundle Tenant_A_VMOTION
|
||||
rd 192.0.255.4:55160
|
||||
route-target both 55160:55160
|
||||
redistribute learned
|
||||
vlan 160
|
||||
!
|
||||
address-family evpn
|
||||
neighbor EVPN-OVERLAY-PEERS activate
|
||||
!
|
||||
address-family ipv4
|
||||
no neighbor EVPN-OVERLAY-PEERS activate
|
||||
neighbor IPv4-UNDERLAY-PEERS activate
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER activate
|
||||
!
|
||||
vrf Tenant_A_OP_Zone
|
||||
rd 192.0.255.4:10
|
||||
route-target import evpn 10:10
|
||||
route-target export evpn 10:10
|
||||
router-id 192.0.255.4
|
||||
neighbor 10.255.251.0 peer group MLAG-IPv4-UNDERLAY-PEER
|
||||
redistribute connected
|
||||
!
|
||||
management api http-commands
|
||||
protocol https
|
||||
no shutdown
|
||||
!
|
||||
vrf default
|
||||
no shutdown
|
||||
!
|
||||
end
|
255
docs/labs/lab06-provisioning/configlets/AVD_leaf3.cfg
Normal file
255
docs/labs/lab06-provisioning/configlets/AVD_leaf3.cfg
Normal file
|
@ -0,0 +1,255 @@
|
|||
!RANCID-CONTENT-TYPE: arista
|
||||
!
|
||||
vlan internal order ascending range 1006 1199
|
||||
!
|
||||
transceiver qsfp default-mode 4x10G
|
||||
!
|
||||
service routing protocols model multi-agent
|
||||
!
|
||||
hostname leaf3
|
||||
ip name-server vrf default 8.8.8.8
|
||||
ip name-server vrf default 192.168.2.1
|
||||
dns domain atd.lab
|
||||
!
|
||||
spanning-tree mode mstp
|
||||
no spanning-tree vlan-id 4093-4094
|
||||
spanning-tree mst 0 priority 16384
|
||||
!
|
||||
no enable password
|
||||
no aaa root
|
||||
!
|
||||
vlan 110
|
||||
name Tenant_A_OP_Zone_1
|
||||
!
|
||||
vlan 160
|
||||
name Tenant_A_VMOTION
|
||||
!
|
||||
vlan 3009
|
||||
name MLAG_iBGP_Tenant_A_OP_Zone
|
||||
trunk group LEAF_PEER_L3
|
||||
!
|
||||
vlan 4093
|
||||
name LEAF_PEER_L3
|
||||
trunk group LEAF_PEER_L3
|
||||
!
|
||||
vlan 4094
|
||||
name MLAG_PEER
|
||||
trunk group MLAG
|
||||
!
|
||||
vrf instance Tenant_A_OP_Zone
|
||||
!
|
||||
interface Port-Channel1
|
||||
description MLAG_PEER_leaf4_Po1
|
||||
no shutdown
|
||||
switchport
|
||||
switchport trunk allowed vlan 2-4094
|
||||
switchport mode trunk
|
||||
switchport trunk group LEAF_PEER_L3
|
||||
switchport trunk group MLAG
|
||||
!
|
||||
interface Port-Channel4
|
||||
description host2_PortChannel
|
||||
no shutdown
|
||||
switchport
|
||||
switchport access vlan 110
|
||||
mlag 4
|
||||
!
|
||||
interface Ethernet1
|
||||
description MLAG_PEER_leaf4_Ethernet1
|
||||
no shutdown
|
||||
channel-group 1 mode active
|
||||
!
|
||||
interface Ethernet2
|
||||
description P2P_LINK_TO_SPINE1_Ethernet4
|
||||
no shutdown
|
||||
mtu 1500
|
||||
no switchport
|
||||
ip address 172.30.255.9/31
|
||||
!
|
||||
interface Ethernet3
|
||||
description P2P_LINK_TO_SPINE2_Ethernet4
|
||||
no shutdown
|
||||
mtu 1500
|
||||
no switchport
|
||||
ip address 172.30.255.11/31
|
||||
!
|
||||
interface Ethernet4
|
||||
description host2_Eth1
|
||||
no shutdown
|
||||
channel-group 4 mode active
|
||||
!
|
||||
interface Ethernet5
|
||||
description host2_Eth2
|
||||
no shutdown
|
||||
channel-group 4 mode active
|
||||
!
|
||||
interface Ethernet6
|
||||
description MLAG_PEER_leaf4_Ethernet6
|
||||
no shutdown
|
||||
channel-group 1 mode active
|
||||
!
|
||||
interface Loopback0
|
||||
description EVPN_Overlay_Peering
|
||||
no shutdown
|
||||
ip address 192.0.255.5/32
|
||||
!
|
||||
interface Loopback1
|
||||
description VTEP_VXLAN_Tunnel_Source
|
||||
no shutdown
|
||||
ip address 192.0.254.5/32
|
||||
!
|
||||
interface Loopback100
|
||||
description Tenant_A_OP_Zone_VTEP_DIAGNOSTICS
|
||||
no shutdown
|
||||
vrf Tenant_A_OP_Zone
|
||||
ip address 10.255.1.5/32
|
||||
!
|
||||
interface Management1
|
||||
description oob_management
|
||||
no shutdown
|
||||
ip address 192.168.0.14/24
|
||||
!
|
||||
interface Vlan110
|
||||
description Tenant_A_OP_Zone_1
|
||||
no shutdown
|
||||
vrf Tenant_A_OP_Zone
|
||||
ip address virtual 10.1.10.1/24
|
||||
!
|
||||
interface Vlan3009
|
||||
description MLAG_PEER_L3_iBGP: vrf Tenant_A_OP_Zone
|
||||
no shutdown
|
||||
mtu 1500
|
||||
vrf Tenant_A_OP_Zone
|
||||
ip address 10.255.251.4/31
|
||||
!
|
||||
interface Vlan4093
|
||||
description MLAG_PEER_L3_PEERING
|
||||
no shutdown
|
||||
mtu 1500
|
||||
ip address 10.255.251.4/31
|
||||
!
|
||||
interface Vlan4094
|
||||
description MLAG_PEER
|
||||
no shutdown
|
||||
mtu 1500
|
||||
no autostate
|
||||
ip address 10.255.252.4/31
|
||||
!
|
||||
interface Vxlan1
|
||||
description leaf3_VTEP
|
||||
vxlan source-interface Loopback1
|
||||
vxlan virtual-router encapsulation mac-address mlag-system-id
|
||||
vxlan udp-port 4789
|
||||
vxlan vlan 110 vni 10110
|
||||
vxlan vlan 160 vni 55160
|
||||
vxlan vrf Tenant_A_OP_Zone vni 10
|
||||
!
|
||||
ip virtual-router mac-address 00:1c:73:00:dc:01
|
||||
!
|
||||
ip address virtual source-nat vrf Tenant_A_OP_Zone address 10.255.1.5
|
||||
!
|
||||
ip routing
|
||||
ip routing vrf Tenant_A_OP_Zone
|
||||
!
|
||||
ip prefix-list PL-LOOPBACKS-EVPN-OVERLAY
|
||||
seq 10 permit 192.0.255.0/24 eq 32
|
||||
seq 20 permit 192.0.254.0/24 eq 32
|
||||
!
|
||||
mlag configuration
|
||||
domain-id pod2
|
||||
local-interface Vlan4094
|
||||
peer-address 10.255.252.5
|
||||
peer-link Port-Channel1
|
||||
reload-delay mlag 300
|
||||
reload-delay non-mlag 330
|
||||
!
|
||||
ip route 0.0.0.0/0 192.168.0.1
|
||||
!
|
||||
route-map RM-CONN-2-BGP permit 10
|
||||
match ip address prefix-list PL-LOOPBACKS-EVPN-OVERLAY
|
||||
!
|
||||
route-map RM-MLAG-PEER-IN permit 10
|
||||
description Make routes learned over MLAG Peer-link less preferred on spines to ensure optimal routing
|
||||
set origin incomplete
|
||||
!
|
||||
router bfd
|
||||
multihop interval 1200 min-rx 1200 multiplier 3
|
||||
!
|
||||
router bgp 65102
|
||||
router-id 192.0.255.5
|
||||
no bgp default ipv4-unicast
|
||||
distance bgp 20 200 200
|
||||
graceful-restart restart-time 300
|
||||
graceful-restart
|
||||
maximum-paths 4 ecmp 4
|
||||
neighbor EVPN-OVERLAY-PEERS peer group
|
||||
neighbor EVPN-OVERLAY-PEERS update-source Loopback0
|
||||
neighbor EVPN-OVERLAY-PEERS bfd
|
||||
neighbor EVPN-OVERLAY-PEERS ebgp-multihop 3
|
||||
neighbor EVPN-OVERLAY-PEERS password 7 q+VNViP5i4rVjW1cxFv2wA==
|
||||
neighbor EVPN-OVERLAY-PEERS send-community
|
||||
neighbor EVPN-OVERLAY-PEERS maximum-routes 0
|
||||
neighbor IPv4-UNDERLAY-PEERS peer group
|
||||
neighbor IPv4-UNDERLAY-PEERS password 7 AQQvKeimxJu+uGQ/yYvv9w==
|
||||
neighbor IPv4-UNDERLAY-PEERS send-community
|
||||
neighbor IPv4-UNDERLAY-PEERS maximum-routes 12000
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER peer group
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER remote-as 65102
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER next-hop-self
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER description leaf4
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER password 7 vnEaG8gMeQf3d3cN6PktXQ==
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER send-community
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER maximum-routes 12000
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER route-map RM-MLAG-PEER-IN in
|
||||
neighbor 10.255.251.5 peer group MLAG-IPv4-UNDERLAY-PEER
|
||||
neighbor 10.255.251.5 description leaf4
|
||||
neighbor 172.30.255.8 peer group IPv4-UNDERLAY-PEERS
|
||||
neighbor 172.30.255.8 remote-as 65001
|
||||
neighbor 172.30.255.8 description spine1_Ethernet4
|
||||
neighbor 172.30.255.10 peer group IPv4-UNDERLAY-PEERS
|
||||
neighbor 172.30.255.10 remote-as 65001
|
||||
neighbor 172.30.255.10 description spine2_Ethernet4
|
||||
neighbor 192.0.255.1 peer group EVPN-OVERLAY-PEERS
|
||||
neighbor 192.0.255.1 remote-as 65001
|
||||
neighbor 192.0.255.1 description spine1
|
||||
neighbor 192.0.255.2 peer group EVPN-OVERLAY-PEERS
|
||||
neighbor 192.0.255.2 remote-as 65001
|
||||
neighbor 192.0.255.2 description spine2
|
||||
redistribute connected route-map RM-CONN-2-BGP
|
||||
!
|
||||
vlan-aware-bundle Tenant_A_OP_Zone
|
||||
rd 192.0.255.5:10
|
||||
route-target both 10:10
|
||||
redistribute learned
|
||||
vlan 110
|
||||
!
|
||||
vlan-aware-bundle Tenant_A_VMOTION
|
||||
rd 192.0.255.5:55160
|
||||
route-target both 55160:55160
|
||||
redistribute learned
|
||||
vlan 160
|
||||
!
|
||||
address-family evpn
|
||||
neighbor EVPN-OVERLAY-PEERS activate
|
||||
!
|
||||
address-family ipv4
|
||||
no neighbor EVPN-OVERLAY-PEERS activate
|
||||
neighbor IPv4-UNDERLAY-PEERS activate
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER activate
|
||||
!
|
||||
vrf Tenant_A_OP_Zone
|
||||
rd 192.0.255.5:10
|
||||
route-target import evpn 10:10
|
||||
route-target export evpn 10:10
|
||||
router-id 192.0.255.5
|
||||
neighbor 10.255.251.5 peer group MLAG-IPv4-UNDERLAY-PEER
|
||||
redistribute connected
|
||||
!
|
||||
management api http-commands
|
||||
protocol https
|
||||
no shutdown
|
||||
!
|
||||
vrf default
|
||||
no shutdown
|
||||
!
|
||||
end
|
255
docs/labs/lab06-provisioning/configlets/AVD_leaf4.cfg
Normal file
255
docs/labs/lab06-provisioning/configlets/AVD_leaf4.cfg
Normal file
|
@ -0,0 +1,255 @@
|
|||
!RANCID-CONTENT-TYPE: arista
|
||||
!
|
||||
vlan internal order ascending range 1006 1199
|
||||
!
|
||||
transceiver qsfp default-mode 4x10G
|
||||
!
|
||||
service routing protocols model multi-agent
|
||||
!
|
||||
hostname leaf4
|
||||
ip name-server vrf default 8.8.8.8
|
||||
ip name-server vrf default 192.168.2.1
|
||||
dns domain atd.lab
|
||||
!
|
||||
spanning-tree mode mstp
|
||||
no spanning-tree vlan-id 4093-4094
|
||||
spanning-tree mst 0 priority 16384
|
||||
!
|
||||
no enable password
|
||||
no aaa root
|
||||
!
|
||||
vlan 110
|
||||
name Tenant_A_OP_Zone_1
|
||||
!
|
||||
vlan 160
|
||||
name Tenant_A_VMOTION
|
||||
!
|
||||
vlan 3009
|
||||
name MLAG_iBGP_Tenant_A_OP_Zone
|
||||
trunk group LEAF_PEER_L3
|
||||
!
|
||||
vlan 4093
|
||||
name LEAF_PEER_L3
|
||||
trunk group LEAF_PEER_L3
|
||||
!
|
||||
vlan 4094
|
||||
name MLAG_PEER
|
||||
trunk group MLAG
|
||||
!
|
||||
vrf instance Tenant_A_OP_Zone
|
||||
!
|
||||
interface Port-Channel1
|
||||
description MLAG_PEER_leaf3_Po1
|
||||
no shutdown
|
||||
switchport
|
||||
switchport trunk allowed vlan 2-4094
|
||||
switchport mode trunk
|
||||
switchport trunk group LEAF_PEER_L3
|
||||
switchport trunk group MLAG
|
||||
!
|
||||
interface Port-Channel4
|
||||
description host2_PortChannel
|
||||
no shutdown
|
||||
switchport
|
||||
switchport access vlan 110
|
||||
mlag 4
|
||||
!
|
||||
interface Ethernet1
|
||||
description MLAG_PEER_leaf3_Ethernet1
|
||||
no shutdown
|
||||
channel-group 1 mode active
|
||||
!
|
||||
interface Ethernet2
|
||||
description P2P_LINK_TO_SPINE1_Ethernet5
|
||||
no shutdown
|
||||
mtu 1500
|
||||
no switchport
|
||||
ip address 172.30.255.13/31
|
||||
!
|
||||
interface Ethernet3
|
||||
description P2P_LINK_TO_SPINE2_Ethernet5
|
||||
no shutdown
|
||||
mtu 1500
|
||||
no switchport
|
||||
ip address 172.30.255.15/31
|
||||
!
|
||||
interface Ethernet4
|
||||
description host2_Eth3
|
||||
no shutdown
|
||||
channel-group 4 mode active
|
||||
!
|
||||
interface Ethernet5
|
||||
description host2_Eth4
|
||||
no shutdown
|
||||
channel-group 4 mode active
|
||||
!
|
||||
interface Ethernet6
|
||||
description MLAG_PEER_leaf3_Ethernet6
|
||||
no shutdown
|
||||
channel-group 1 mode active
|
||||
!
|
||||
interface Loopback0
|
||||
description EVPN_Overlay_Peering
|
||||
no shutdown
|
||||
ip address 192.0.255.6/32
|
||||
!
|
||||
interface Loopback1
|
||||
description VTEP_VXLAN_Tunnel_Source
|
||||
no shutdown
|
||||
ip address 192.0.254.5/32
|
||||
!
|
||||
interface Loopback100
|
||||
description Tenant_A_OP_Zone_VTEP_DIAGNOSTICS
|
||||
no shutdown
|
||||
vrf Tenant_A_OP_Zone
|
||||
ip address 10.255.1.6/32
|
||||
!
|
||||
interface Management1
|
||||
description oob_management
|
||||
no shutdown
|
||||
ip address 192.168.0.15/24
|
||||
!
|
||||
interface Vlan110
|
||||
description Tenant_A_OP_Zone_1
|
||||
no shutdown
|
||||
vrf Tenant_A_OP_Zone
|
||||
ip address virtual 10.1.10.1/24
|
||||
!
|
||||
interface Vlan3009
|
||||
description MLAG_PEER_L3_iBGP: vrf Tenant_A_OP_Zone
|
||||
no shutdown
|
||||
mtu 1500
|
||||
vrf Tenant_A_OP_Zone
|
||||
ip address 10.255.251.5/31
|
||||
!
|
||||
interface Vlan4093
|
||||
description MLAG_PEER_L3_PEERING
|
||||
no shutdown
|
||||
mtu 1500
|
||||
ip address 10.255.251.5/31
|
||||
!
|
||||
interface Vlan4094
|
||||
description MLAG_PEER
|
||||
no shutdown
|
||||
mtu 1500
|
||||
no autostate
|
||||
ip address 10.255.252.5/31
|
||||
!
|
||||
interface Vxlan1
|
||||
description leaf4_VTEP
|
||||
vxlan source-interface Loopback1
|
||||
vxlan virtual-router encapsulation mac-address mlag-system-id
|
||||
vxlan udp-port 4789
|
||||
vxlan vlan 110 vni 10110
|
||||
vxlan vlan 160 vni 55160
|
||||
vxlan vrf Tenant_A_OP_Zone vni 10
|
||||
!
|
||||
ip virtual-router mac-address 00:1c:73:00:dc:01
|
||||
!
|
||||
ip address virtual source-nat vrf Tenant_A_OP_Zone address 10.255.1.6
|
||||
!
|
||||
ip routing
|
||||
ip routing vrf Tenant_A_OP_Zone
|
||||
!
|
||||
ip prefix-list PL-LOOPBACKS-EVPN-OVERLAY
|
||||
seq 10 permit 192.0.255.0/24 eq 32
|
||||
seq 20 permit 192.0.254.0/24 eq 32
|
||||
!
|
||||
mlag configuration
|
||||
domain-id pod2
|
||||
local-interface Vlan4094
|
||||
peer-address 10.255.252.4
|
||||
peer-link Port-Channel1
|
||||
reload-delay mlag 300
|
||||
reload-delay non-mlag 330
|
||||
!
|
||||
ip route 0.0.0.0/0 192.168.0.1
|
||||
!
|
||||
route-map RM-CONN-2-BGP permit 10
|
||||
match ip address prefix-list PL-LOOPBACKS-EVPN-OVERLAY
|
||||
!
|
||||
route-map RM-MLAG-PEER-IN permit 10
|
||||
description Make routes learned over MLAG Peer-link less preferred on spines to ensure optimal routing
|
||||
set origin incomplete
|
||||
!
|
||||
router bfd
|
||||
multihop interval 1200 min-rx 1200 multiplier 3
|
||||
!
|
||||
router bgp 65102
|
||||
router-id 192.0.255.6
|
||||
no bgp default ipv4-unicast
|
||||
distance bgp 20 200 200
|
||||
graceful-restart restart-time 300
|
||||
graceful-restart
|
||||
maximum-paths 4 ecmp 4
|
||||
neighbor EVPN-OVERLAY-PEERS peer group
|
||||
neighbor EVPN-OVERLAY-PEERS update-source Loopback0
|
||||
neighbor EVPN-OVERLAY-PEERS bfd
|
||||
neighbor EVPN-OVERLAY-PEERS ebgp-multihop 3
|
||||
neighbor EVPN-OVERLAY-PEERS password 7 q+VNViP5i4rVjW1cxFv2wA==
|
||||
neighbor EVPN-OVERLAY-PEERS send-community
|
||||
neighbor EVPN-OVERLAY-PEERS maximum-routes 0
|
||||
neighbor IPv4-UNDERLAY-PEERS peer group
|
||||
neighbor IPv4-UNDERLAY-PEERS password 7 AQQvKeimxJu+uGQ/yYvv9w==
|
||||
neighbor IPv4-UNDERLAY-PEERS send-community
|
||||
neighbor IPv4-UNDERLAY-PEERS maximum-routes 12000
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER peer group
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER remote-as 65102
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER next-hop-self
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER description leaf3
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER password 7 vnEaG8gMeQf3d3cN6PktXQ==
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER send-community
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER maximum-routes 12000
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER route-map RM-MLAG-PEER-IN in
|
||||
neighbor 10.255.251.4 peer group MLAG-IPv4-UNDERLAY-PEER
|
||||
neighbor 10.255.251.4 description leaf3
|
||||
neighbor 172.30.255.12 peer group IPv4-UNDERLAY-PEERS
|
||||
neighbor 172.30.255.12 remote-as 65001
|
||||
neighbor 172.30.255.12 description spine1_Ethernet5
|
||||
neighbor 172.30.255.14 peer group IPv4-UNDERLAY-PEERS
|
||||
neighbor 172.30.255.14 remote-as 65001
|
||||
neighbor 172.30.255.14 description spine2_Ethernet5
|
||||
neighbor 192.0.255.1 peer group EVPN-OVERLAY-PEERS
|
||||
neighbor 192.0.255.1 remote-as 65001
|
||||
neighbor 192.0.255.1 description spine1
|
||||
neighbor 192.0.255.2 peer group EVPN-OVERLAY-PEERS
|
||||
neighbor 192.0.255.2 remote-as 65001
|
||||
neighbor 192.0.255.2 description spine2
|
||||
redistribute connected route-map RM-CONN-2-BGP
|
||||
!
|
||||
vlan-aware-bundle Tenant_A_OP_Zone
|
||||
rd 192.0.255.6:10
|
||||
route-target both 10:10
|
||||
redistribute learned
|
||||
vlan 110
|
||||
!
|
||||
vlan-aware-bundle Tenant_A_VMOTION
|
||||
rd 192.0.255.6:55160
|
||||
route-target both 55160:55160
|
||||
redistribute learned
|
||||
vlan 160
|
||||
!
|
||||
address-family evpn
|
||||
neighbor EVPN-OVERLAY-PEERS activate
|
||||
!
|
||||
address-family ipv4
|
||||
no neighbor EVPN-OVERLAY-PEERS activate
|
||||
neighbor IPv4-UNDERLAY-PEERS activate
|
||||
neighbor MLAG-IPv4-UNDERLAY-PEER activate
|
||||
!
|
||||
vrf Tenant_A_OP_Zone
|
||||
rd 192.0.255.6:10
|
||||
route-target import evpn 10:10
|
||||
route-target export evpn 10:10
|
||||
router-id 192.0.255.6
|
||||
neighbor 10.255.251.4 peer group MLAG-IPv4-UNDERLAY-PEER
|
||||
redistribute connected
|
||||
!
|
||||
management api http-commands
|
||||
protocol https
|
||||
no shutdown
|
||||
!
|
||||
vrf default
|
||||
no shutdown
|
||||
!
|
||||
end
|
129
docs/labs/lab06-provisioning/configlets/AVD_spine1.cfg
Normal file
129
docs/labs/lab06-provisioning/configlets/AVD_spine1.cfg
Normal file
|
@ -0,0 +1,129 @@
|
|||
!RANCID-CONTENT-TYPE: arista
|
||||
!
|
||||
vlan internal order ascending range 1006 1199
|
||||
!
|
||||
transceiver qsfp default-mode 4x10G
|
||||
!
|
||||
service routing protocols model multi-agent
|
||||
!
|
||||
hostname spine1
|
||||
ip name-server vrf default 8.8.8.8
|
||||
ip name-server vrf default 192.168.2.1
|
||||
dns domain atd.lab
|
||||
!
|
||||
spanning-tree mode none
|
||||
!
|
||||
no enable password
|
||||
no aaa root
|
||||
!
|
||||
interface Ethernet2
|
||||
description P2P_LINK_TO_LEAF1_Ethernet2
|
||||
no shutdown
|
||||
mtu 1500
|
||||
no switchport
|
||||
ip address 172.30.255.0/31
|
||||
!
|
||||
interface Ethernet3
|
||||
description P2P_LINK_TO_LEAF2_Ethernet2
|
||||
no shutdown
|
||||
mtu 1500
|
||||
no switchport
|
||||
ip address 172.30.255.4/31
|
||||
!
|
||||
interface Ethernet4
|
||||
description P2P_LINK_TO_LEAF3_Ethernet2
|
||||
no shutdown
|
||||
mtu 1500
|
||||
no switchport
|
||||
ip address 172.30.255.8/31
|
||||
!
|
||||
interface Ethernet5
|
||||
description P2P_LINK_TO_LEAF4_Ethernet2
|
||||
no shutdown
|
||||
mtu 1500
|
||||
no switchport
|
||||
ip address 172.30.255.12/31
|
||||
!
|
||||
interface Loopback0
|
||||
description EVPN_Overlay_Peering
|
||||
no shutdown
|
||||
ip address 192.0.255.1/32
|
||||
!
|
||||
interface Management1
|
||||
description oob_management
|
||||
no shutdown
|
||||
ip address 192.168.0.10/24
|
||||
!
|
||||
ip routing
|
||||
!
|
||||
ip prefix-list PL-LOOPBACKS-EVPN-OVERLAY
|
||||
seq 10 permit 192.0.255.0/24 eq 32
|
||||
!
|
||||
ip route 0.0.0.0/0 192.168.0.1
|
||||
!
|
||||
route-map RM-CONN-2-BGP permit 10
|
||||
match ip address prefix-list PL-LOOPBACKS-EVPN-OVERLAY
|
||||
!
|
||||
router bfd
|
||||
multihop interval 1200 min-rx 1200 multiplier 3
|
||||
!
|
||||
router bgp 65001
|
||||
router-id 192.0.255.1
|
||||
no bgp default ipv4-unicast
|
||||
distance bgp 20 200 200
|
||||
graceful-restart restart-time 300
|
||||
graceful-restart
|
||||
maximum-paths 4 ecmp 4
|
||||
neighbor EVPN-OVERLAY-PEERS peer group
|
||||
neighbor EVPN-OVERLAY-PEERS next-hop-unchanged
|
||||
neighbor EVPN-OVERLAY-PEERS update-source Loopback0
|
||||
neighbor EVPN-OVERLAY-PEERS bfd
|
||||
neighbor EVPN-OVERLAY-PEERS ebgp-multihop 3
|
||||
neighbor EVPN-OVERLAY-PEERS password 7 q+VNViP5i4rVjW1cxFv2wA==
|
||||
neighbor EVPN-OVERLAY-PEERS send-community
|
||||
neighbor EVPN-OVERLAY-PEERS maximum-routes 0
|
||||
neighbor IPv4-UNDERLAY-PEERS peer group
|
||||
neighbor IPv4-UNDERLAY-PEERS password 7 AQQvKeimxJu+uGQ/yYvv9w==
|
||||
neighbor IPv4-UNDERLAY-PEERS send-community
|
||||
neighbor IPv4-UNDERLAY-PEERS maximum-routes 12000
|
||||
neighbor 172.30.255.1 peer group IPv4-UNDERLAY-PEERS
|
||||
neighbor 172.30.255.1 remote-as 65101
|
||||
neighbor 172.30.255.1 description leaf1_Ethernet2
|
||||
neighbor 172.30.255.5 peer group IPv4-UNDERLAY-PEERS
|
||||
neighbor 172.30.255.5 remote-as 65101
|
||||
neighbor 172.30.255.5 description leaf2_Ethernet2
|
||||
neighbor 172.30.255.9 peer group IPv4-UNDERLAY-PEERS
|
||||
neighbor 172.30.255.9 remote-as 65102
|
||||
neighbor 172.30.255.9 description leaf3_Ethernet2
|
||||
neighbor 172.30.255.13 peer group IPv4-UNDERLAY-PEERS
|
||||
neighbor 172.30.255.13 remote-as 65102
|
||||
neighbor 172.30.255.13 description leaf4_Ethernet2
|
||||
neighbor 192.0.255.3 peer group EVPN-OVERLAY-PEERS
|
||||
neighbor 192.0.255.3 remote-as 65101
|
||||
neighbor 192.0.255.3 description leaf1
|
||||
neighbor 192.0.255.4 peer group EVPN-OVERLAY-PEERS
|
||||
neighbor 192.0.255.4 remote-as 65101
|
||||
neighbor 192.0.255.4 description leaf2
|
||||
neighbor 192.0.255.5 peer group EVPN-OVERLAY-PEERS
|
||||
neighbor 192.0.255.5 remote-as 65102
|
||||
neighbor 192.0.255.5 description leaf3
|
||||
neighbor 192.0.255.6 peer group EVPN-OVERLAY-PEERS
|
||||
neighbor 192.0.255.6 remote-as 65102
|
||||
neighbor 192.0.255.6 description leaf4
|
||||
redistribute connected route-map RM-CONN-2-BGP
|
||||
!
|
||||
address-family evpn
|
||||
neighbor EVPN-OVERLAY-PEERS activate
|
||||
!
|
||||
address-family ipv4
|
||||
no neighbor EVPN-OVERLAY-PEERS activate
|
||||
neighbor IPv4-UNDERLAY-PEERS activate
|
||||
!
|
||||
management api http-commands
|
||||
protocol https
|
||||
no shutdown
|
||||
!
|
||||
vrf default
|
||||
no shutdown
|
||||
!
|
||||
end
|
129
docs/labs/lab06-provisioning/configlets/AVD_spine2.cfg
Normal file
129
docs/labs/lab06-provisioning/configlets/AVD_spine2.cfg
Normal file
|
@ -0,0 +1,129 @@
|
|||
!RANCID-CONTENT-TYPE: arista
|
||||
!
|
||||
vlan internal order ascending range 1006 1199
|
||||
!
|
||||
transceiver qsfp default-mode 4x10G
|
||||
!
|
||||
service routing protocols model multi-agent
|
||||
!
|
||||
hostname spine2
|
||||
ip name-server vrf default 8.8.8.8
|
||||
ip name-server vrf default 192.168.2.1
|
||||
dns domain atd.lab
|
||||
!
|
||||
spanning-tree mode none
|
||||
!
|
||||
no enable password
|
||||
no aaa root
|
||||
!
|
||||
interface Ethernet2
|
||||
description P2P_LINK_TO_LEAF1_Ethernet3
|
||||
no shutdown
|
||||
mtu 1500
|
||||
no switchport
|
||||
ip address 172.30.255.2/31
|
||||
!
|
||||
interface Ethernet3
|
||||
description P2P_LINK_TO_LEAF2_Ethernet3
|
||||
no shutdown
|
||||
mtu 1500
|
||||
no switchport
|
||||
ip address 172.30.255.6/31
|
||||
!
|
||||
interface Ethernet4
|
||||
description P2P_LINK_TO_LEAF3_Ethernet3
|
||||
no shutdown
|
||||
mtu 1500
|
||||
no switchport
|
||||
ip address 172.30.255.10/31
|
||||
!
|
||||
interface Ethernet5
|
||||
description P2P_LINK_TO_LEAF4_Ethernet3
|
||||
no shutdown
|
||||
mtu 1500
|
||||
no switchport
|
||||
ip address 172.30.255.14/31
|
||||
!
|
||||
interface Loopback0
|
||||
description EVPN_Overlay_Peering
|
||||
no shutdown
|
||||
ip address 192.0.255.2/32
|
||||
!
|
||||
interface Management1
|
||||
description oob_management
|
||||
no shutdown
|
||||
ip address 192.168.0.11/24
|
||||
!
|
||||
ip routing
|
||||
!
|
||||
ip prefix-list PL-LOOPBACKS-EVPN-OVERLAY
|
||||
seq 10 permit 192.0.255.0/24 eq 32
|
||||
!
|
||||
ip route 0.0.0.0/0 192.168.0.1
|
||||
!
|
||||
route-map RM-CONN-2-BGP permit 10
|
||||
match ip address prefix-list PL-LOOPBACKS-EVPN-OVERLAY
|
||||
!
|
||||
router bfd
|
||||
multihop interval 1200 min-rx 1200 multiplier 3
|
||||
!
|
||||
router bgp 65001
|
||||
router-id 192.0.255.2
|
||||
no bgp default ipv4-unicast
|
||||
distance bgp 20 200 200
|
||||
graceful-restart restart-time 300
|
||||
graceful-restart
|
||||
maximum-paths 4 ecmp 4
|
||||
neighbor EVPN-OVERLAY-PEERS peer group
|
||||
neighbor EVPN-OVERLAY-PEERS next-hop-unchanged
|
||||
neighbor EVPN-OVERLAY-PEERS update-source Loopback0
|
||||
neighbor EVPN-OVERLAY-PEERS bfd
|
||||
neighbor EVPN-OVERLAY-PEERS ebgp-multihop 3
|
||||
neighbor EVPN-OVERLAY-PEERS password 7 q+VNViP5i4rVjW1cxFv2wA==
|
||||
neighbor EVPN-OVERLAY-PEERS send-community
|
||||
neighbor EVPN-OVERLAY-PEERS maximum-routes 0
|
||||
neighbor IPv4-UNDERLAY-PEERS peer group
|
||||
neighbor IPv4-UNDERLAY-PEERS password 7 AQQvKeimxJu+uGQ/yYvv9w==
|
||||
neighbor IPv4-UNDERLAY-PEERS send-community
|
||||
neighbor IPv4-UNDERLAY-PEERS maximum-routes 12000
|
||||
neighbor 172.30.255.3 peer group IPv4-UNDERLAY-PEERS
|
||||
neighbor 172.30.255.3 remote-as 65101
|
||||
neighbor 172.30.255.3 description leaf1_Ethernet3
|
||||
neighbor 172.30.255.7 peer group IPv4-UNDERLAY-PEERS
|
||||
neighbor 172.30.255.7 remote-as 65101
|
||||
neighbor 172.30.255.7 description leaf2_Ethernet3
|
||||
neighbor 172.30.255.11 peer group IPv4-UNDERLAY-PEERS
|
||||
neighbor 172.30.255.11 remote-as 65102
|
||||
neighbor 172.30.255.11 description leaf3_Ethernet3
|
||||
neighbor 172.30.255.15 peer group IPv4-UNDERLAY-PEERS
|
||||
neighbor 172.30.255.15 remote-as 65102
|
||||
neighbor 172.30.255.15 description leaf4_Ethernet3
|
||||
neighbor 192.0.255.3 peer group EVPN-OVERLAY-PEERS
|
||||
neighbor 192.0.255.3 remote-as 65101
|
||||
neighbor 192.0.255.3 description leaf1
|
||||
neighbor 192.0.255.4 peer group EVPN-OVERLAY-PEERS
|
||||
neighbor 192.0.255.4 remote-as 65101
|
||||
neighbor 192.0.255.4 description leaf2
|
||||
neighbor 192.0.255.5 peer group EVPN-OVERLAY-PEERS
|
||||
neighbor 192.0.255.5 remote-as 65102
|
||||
neighbor 192.0.255.5 description leaf3
|
||||
neighbor 192.0.255.6 peer group EVPN-OVERLAY-PEERS
|
||||
neighbor 192.0.255.6 remote-as 65102
|
||||
neighbor 192.0.255.6 description leaf4
|
||||
redistribute connected route-map RM-CONN-2-BGP
|
||||
!
|
||||
address-family evpn
|
||||
neighbor EVPN-OVERLAY-PEERS activate
|
||||
!
|
||||
address-family ipv4
|
||||
no neighbor EVPN-OVERLAY-PEERS activate
|
||||
neighbor IPv4-UNDERLAY-PEERS activate
|
||||
!
|
||||
management api http-commands
|
||||
protocol https
|
||||
no shutdown
|
||||
!
|
||||
vrf default
|
||||
no shutdown
|
||||
!
|
||||
end
|
63
docs/labs/lab06-provisioning/gen_builder.py
Normal file
63
docs/labs/lab06-provisioning/gen_builder.py
Normal file
|
@ -0,0 +1,63 @@
|
|||
# Copyright (c) 2020 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision
|
||||
clnt = CvpClient()
|
||||
clnt.connect(['cvp1'],'username', 'password')
|
||||
|
||||
container_id = clnt.api.get_container_by_name("TP_LEAFS")['key']
|
||||
builder_name = 'SYS_TelemetryBuilderV3'
|
||||
configletBuilderID = clnt.api.get_configlet_by_name(builder_name)['key']
|
||||
|
||||
payload = {"previewValues":[{
|
||||
"fieldId":"vrf",
|
||||
"value":"red"}],
|
||||
"configletBuilderId":configletBuilderID,
|
||||
"netElementIds":[],
|
||||
"pageType":"container",
|
||||
"containerId":container_id,
|
||||
"containerToId":"",
|
||||
"mode":"assign"}
|
||||
|
||||
preview = clnt.post('/configlet/configletBuilderPreview.do', data=payload)
|
||||
|
||||
generated_names_list = []
|
||||
generated_keys_list = []
|
||||
|
||||
for i in preview['data']:
|
||||
generated_names_list.append(i['configlet']['name'])
|
||||
generated_keys_list.append(i['configlet']['key'])
|
||||
|
||||
clnt.get("/configlet/searchConfiglets.do?objectId={}&objectType=container&type=ignoreDraft&queryparam={}&startIndex=0&endIndex=22&sortByColumn=&sortOrder=".format(container_id, builder_name.lower()))
|
||||
|
||||
tempData = {"data":[{
|
||||
"info":"Configlet Assign: to container TP_LEAFS",
|
||||
"infoPreview":"<b>Configlet Assign:</b> to container TP_LEAFS",
|
||||
"action":"associate",
|
||||
"nodeType":"configlet",
|
||||
"nodeId":"",
|
||||
"toId":container_id,
|
||||
"fromId":"","nodeName":"","fromName":"",
|
||||
"toName":"TP_LEAFS",
|
||||
"toIdType":"container",
|
||||
"configletList":generated_keys_list,
|
||||
"configletNamesList":generated_names_list,
|
||||
"ignoreConfigletList":[],
|
||||
"ignoreConfigletNamesList":[],
|
||||
"configletBuilderList":[configletBuilderID],
|
||||
"configletBuilderNamesList":[builder_name],
|
||||
"ignoreConfigletBuilderList":[],
|
||||
"ignoreConfigletBuilderNamesList":[]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
clnt.api._add_temp_action(tempData)
|
||||
clnt.api._save_topology_v2([])
|
220
docs/labs/lab06-provisioning/mlag_issu.py
Normal file
220
docs/labs/lab06-provisioning/mlag_issu.py
Normal file
|
@ -0,0 +1,220 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# python3 mlag_issu <upgrade inventory file> <MLAG peer to upgrade: 'peer1' or 'peer2'>"
|
||||
#
|
||||
# # Example of upgrade inventory file (YAML)
|
||||
# cvp_hosts:
|
||||
# - 192.168.0.191
|
||||
# - 192.168.0.192
|
||||
# - 192.168.0.193
|
||||
# cvp_username: cvpadmin
|
||||
# target_eos_version: 4.25.4M
|
||||
# target_terminattr_version: 1.13.6
|
||||
# mlag_couples:
|
||||
# - peer1: leaf101-1
|
||||
# peer2: leaf101-2
|
||||
# - peer1: leaf102-1
|
||||
# peer2: leaf102-2
|
||||
#
|
||||
# Note: upgrades are performed in parallel
|
||||
|
||||
import sys
|
||||
import time
|
||||
import string
|
||||
import random
|
||||
from getpass import getpass
|
||||
import requests
|
||||
from requests.packages.urllib3.exceptions import InsecureRequestWarning
|
||||
from datetime import datetime
|
||||
from cvprac.cvp_client import CvpClient
|
||||
from cvprac.cvp_client_errors import CvpLoginError, CvpApiError
|
||||
from pprint import pprint
|
||||
from operator import itemgetter
|
||||
import yaml
|
||||
|
||||
class CvpDeviceUpgrader(object):
|
||||
def __init__(self, hosts, username, password):
|
||||
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
|
||||
self.cvp_hosts = hosts
|
||||
self.cvp_user = username
|
||||
self.cvp_password = password
|
||||
self.session = self._open_cvp_session()
|
||||
|
||||
def _open_cvp_session(self):
|
||||
try:
|
||||
client = CvpClient()
|
||||
client.connect(
|
||||
nodes=self.cvp_hosts,
|
||||
username=self.cvp_user,
|
||||
password=self.cvp_password,
|
||||
request_timeout=300,
|
||||
connect_timeout=30
|
||||
)
|
||||
return(client)
|
||||
except CvpLoginError as e:
|
||||
print(f"Cannot connect to CVP API: {e}")
|
||||
exit()
|
||||
|
||||
def create_mlag_issu_change_control(self, taskIDs, deviceIDs):
|
||||
cc_id = f"CC_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
||||
pre_upgrade_stage = {'stage': [{
|
||||
'id': f"preU_{cc_id}",
|
||||
'name': 'pre_upgrade',
|
||||
'stage_row':[{'stage': [{
|
||||
'id': ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(9)),
|
||||
'action': {
|
||||
'name': 'mlaghealthcheck',
|
||||
'timeout': 0,
|
||||
'args': {
|
||||
'DeviceID': device_id
|
||||
}
|
||||
}
|
||||
} for device_id in deviceIDs]}]
|
||||
}]}
|
||||
upgrade_stage = {'stage': [{
|
||||
'id': f"U_{cc_id}",
|
||||
'name': 'upgrade',
|
||||
'stage_row': [{'stage': [{
|
||||
'id': task_id,
|
||||
'action': {
|
||||
'name': 'task',
|
||||
'args': {
|
||||
'TaskID': task_id
|
||||
}
|
||||
}
|
||||
} for task_id in taskIDs]}]
|
||||
}]}
|
||||
post_upgrade_stage = {'stage': [{
|
||||
'id': f"postU_{cc_id}",
|
||||
'name': 'post_upgrade',
|
||||
'stage_row': [{'stage': [{
|
||||
'id': ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(9)),
|
||||
'action': {
|
||||
'name': 'mlaghealthcheck',
|
||||
'timeout': 0,
|
||||
'args': {
|
||||
'DeviceID': device_id
|
||||
}
|
||||
}
|
||||
} for device_id in deviceIDs]}]
|
||||
}]}
|
||||
cc_data = {'config': {
|
||||
'id': cc_id,
|
||||
'name': f"Change Control {cc_id}",
|
||||
'root_stage': {
|
||||
'id': 'root',
|
||||
'name': f"Change Control {cc_id} root",
|
||||
'stage_row': [pre_upgrade_stage, upgrade_stage, post_upgrade_stage],
|
||||
}
|
||||
}}
|
||||
try:
|
||||
res = self.session.post('/api/v3/services/ccapi.ChangeControl/Update',
|
||||
data=cc_data,
|
||||
timeout=self.session.api.request_timeout
|
||||
)
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
return(None)
|
||||
print(f"Change control {res[0]['id']} created at {res[0]['update_timestamp']}")
|
||||
return(res[0]['id'])
|
||||
|
||||
def get_mlag_issu_change_control_logs(self, ccID, startTime):
|
||||
end_time = int(time.time() * 1000)
|
||||
cc_logs_data = {'category': 'ChangeControl',
|
||||
'objectKey': ccID,
|
||||
'dataSize': 15000,
|
||||
'startTime': startTime,
|
||||
'endTime': end_time
|
||||
}
|
||||
logs = self.session.post('/cvpservice/audit/getLogs.do',
|
||||
data=cc_logs_data,
|
||||
timeout=self.session.api.request_timeout
|
||||
)
|
||||
for log in sorted(logs['data'], key=itemgetter('dateTimeInLongFormat')):
|
||||
if log['subObjectName'] and 'Command(s)' not in log['activity']:
|
||||
log_date = datetime.fromtimestamp(log['dateTimeInLongFormat']/1000)
|
||||
print(f"{log_date} {log['subObjectName']}: {log['activity']}")
|
||||
return(end_time + 1)
|
||||
|
||||
def run_mlag_issu_change_control(self, ccID):
|
||||
print(f"Automatic approval of change control {ccID}")
|
||||
self.session.api.approve_change_control(ccID, datetime.utcnow().isoformat() + 'Z')
|
||||
time.sleep(2)
|
||||
print(f"Starting the execution of change control {ccID}")
|
||||
start_time = int(time.time() * 1000)
|
||||
self.session.api.execute_change_controls([ccID])
|
||||
time.sleep(2)
|
||||
cc_status = self.session.api.get_change_control_status(ccID)[0]['status']
|
||||
start_time = self.get_mlag_issu_change_control_logs(ccID, start_time)
|
||||
while cc_status['state'] == 'Running':
|
||||
time.sleep(30)
|
||||
cc_status = self.session.api.get_change_control_status(ccID)[0]['status']
|
||||
start_time = self.get_mlag_issu_change_control_logs(ccID, start_time)
|
||||
print(f"Change control {ccID} final status: {cc_status['state']}")
|
||||
if cc_status['error']:
|
||||
print(f"Change control {ccID} had the following errors: {cc_status['error']}")
|
||||
else:
|
||||
print(f"Change control {ccID} completed without errors")
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 3:
|
||||
print(f"Usage: python3 {sys.argv[0]} <input file path> <MLAG peer to upgrade: peer1/peer2>")
|
||||
exit()
|
||||
try:
|
||||
with open(sys.argv[1], 'r') as yf:
|
||||
params = yaml.safe_load(yf)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
exit()
|
||||
cvp_password = getpass(prompt=f"CVP password for user {params['cvp_username']}: ")
|
||||
cvpdu = CvpDeviceUpgrader(
|
||||
hosts=params['cvp_hosts'],
|
||||
username=params['cvp_username'],
|
||||
password=cvp_password
|
||||
)
|
||||
image_bundle = None
|
||||
for bundle in cvpdu.session.api.get_image_bundles()['data']:
|
||||
eos_match = False
|
||||
terminattr_match = False
|
||||
for img in bundle['imageIds']:
|
||||
if params['target_eos_version'] in img:
|
||||
eos_match = True
|
||||
elif params['target_terminattr_version'] in img:
|
||||
terminattr_match = True
|
||||
if eos_match and terminattr_match:
|
||||
image_bundle = bundle
|
||||
break
|
||||
if image_bundle is None:
|
||||
print(f"Cannot find an image bundle with EOS {params['target_eos_version']} and TerminAttr {params['target_terminattr_version']}")
|
||||
exit()
|
||||
hostnames = [couple[sys.argv[2]] for couple in params['mlag_couples']]
|
||||
devices_to_upgrade = list()
|
||||
inventory = cvpdu.session.api.get_inventory()
|
||||
for hostname in hostnames:
|
||||
provisioned = False
|
||||
for dev in inventory:
|
||||
if dev['hostname'] == hostname:
|
||||
provisioned = True
|
||||
devices_to_upgrade.append(dev)
|
||||
break
|
||||
if not provisioned:
|
||||
print(f"Device with hostname {hostname} is not provisioned in CVP")
|
||||
if not devices_to_upgrade:
|
||||
print('none of the mentioned devices is provisioned in CVP')
|
||||
exit()
|
||||
print(f"Devices to upgrade: {', '.join([dev['hostname'] for dev in devices_to_upgrade])}")
|
||||
task_ids = list()
|
||||
for device in devices_to_upgrade:
|
||||
response = cvpdu.session.api.apply_image_to_device(image_bundle, device)['data']
|
||||
if response['status'] == 'success':
|
||||
task_ids.extend(response['taskIds'])
|
||||
device_ids = [device['serialNumber'] for device in devices_to_upgrade]
|
||||
cc_id = cvpdu.create_mlag_issu_change_control(task_ids, device_ids)
|
||||
if cc_id is None:
|
||||
print('Failed to create the MLAG ISSU change control')
|
||||
exit()
|
||||
time.sleep(2)
|
||||
cvpdu.run_mlag_issu_change_control(cc_id)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
24
docs/labs/lab06-provisioning/move_device.py
Normal file
24
docs/labs/lab06-provisioning/move_device.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision
|
||||
with open("token.tok") as f:
|
||||
token = f.read().strip('\n')
|
||||
|
||||
clnt = CvpClient()
|
||||
clnt.connect(nodes=['cvp1'], username='',password='',api_token=token)
|
||||
|
||||
container = clnt.api.get_container_by_name('TP_LEAFS') # container object
|
||||
|
||||
app_name = "my app" # can be any string
|
||||
|
||||
device = {"key":"00:1c:73:c5:4c:87", "fqdn":"co633.ire.aristanetworks.com"}
|
||||
|
||||
move_device_to_container(app_name, device, container)
|
115
docs/labs/lab06-provisioning/vc_task_retrigger.py
Normal file
115
docs/labs/lab06-provisioning/vc_task_retrigger.py
Normal file
|
@ -0,0 +1,115 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
# Example on how to re-trigger task creation if a config push task was previously
|
||||
# cancelled and the device is still config out of sync
|
||||
import argparse
|
||||
import ssl
|
||||
import sys
|
||||
from pkg_resources import parse_version
|
||||
from getpass import getpass
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
|
||||
if ((sys.version_info.major == 3) or
|
||||
(sys.version_info.major == 2 and sys.version_info.minor == 7 and
|
||||
sys.version_info.micro >= 5)):
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
compliance = {"0001": "Config is out of sync",
|
||||
"0003": "Config & image out of sync",
|
||||
"0004": "Config, Image and Device time are in sync",
|
||||
"0005": "Device is not reachable",
|
||||
"0008": "Config, Image and Extensions are out of sync",
|
||||
"0009": "Config and Extensions are out of sync",
|
||||
"0012": "Config, Image, Extension and Device time are out of sync",
|
||||
"0013": "Config, Image and Device time are out of sync",
|
||||
"0014": "Config, Extensions and Device time are out of sync",
|
||||
"0016": "Config and Device time are out of sync"
|
||||
}
|
||||
# Create connection to CloudVision
|
||||
clnt = CvpClient()
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Script to recreate a task, if a previous config push was cancelled')
|
||||
parser.add_argument('-u', '--username', default='username')
|
||||
parser.add_argument('-p', '--password', default=None)
|
||||
parser.add_argument('-c', '--cvpserver', action='append')
|
||||
parser.add_argument('-f', '--filter', action='append', default=None)
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.password is None:
|
||||
args.password = getpass()
|
||||
|
||||
for cvpserver in args.cvpserver:
|
||||
print("Connecting to %s" % cvpserver)
|
||||
try:
|
||||
clnt.connect(nodes=[cvpserver], username=args.username, password=args.password)
|
||||
except Exception as e:
|
||||
print("Unable to connect to CVP: %s" % str(e))
|
||||
|
||||
# Get the current CVP version
|
||||
cvp_release = clnt.api.get_cvp_info()['version']
|
||||
if parse_version(cvp_release) < parse_version('2020.3.0'):
|
||||
# For older CVP, we manually trigger a compliance check
|
||||
try:
|
||||
clnt.api.check_compliance('root', 'container')
|
||||
except:
|
||||
# Bad practice, but the check compliance applied to a container can't actually work
|
||||
# since the complianceIndication key doesn't exist on the container level
|
||||
pass
|
||||
else:
|
||||
# with continuous compliance checks, triggering the check is no longer required
|
||||
pass
|
||||
|
||||
device_filters = []
|
||||
if args.filter is not None:
|
||||
for entry in args.filter:
|
||||
device_filters.extend(entry.split(','))
|
||||
|
||||
# Get inventory
|
||||
print("Collecting inventory...")
|
||||
devices = clnt.api.get_inventory()
|
||||
print("%d devices in inventory" % len(devices) )
|
||||
|
||||
for switch in devices:
|
||||
if (switch['status'] == 'Registered' and
|
||||
switch['parentContainerId'] != 'undefined_container'):
|
||||
|
||||
if len(device_filters) > 0:
|
||||
# iterate over device filters, and update task for
|
||||
# any devices not in compliance
|
||||
|
||||
for filter_term in device_filters:
|
||||
print("Checking device: %s" % switch['hostname'])
|
||||
if filter_term in switch['hostname']:
|
||||
# generate configlet list
|
||||
cl = clnt.api.get_configlets_by_device_id(switch['systemMacAddress'])
|
||||
# generate a task if config is out of sync
|
||||
if switch['complianceCode'] in compliance.keys():
|
||||
print(clnt.api.apply_configlets_to_device("", switch, cl))
|
||||
else:
|
||||
print("%s is compliant, nothing to do" % switch['hostname'])
|
||||
else:
|
||||
print("Skipping %s due to filter" % switch['hostname'])
|
||||
else:
|
||||
print("Checking device: %s" % switch['hostname'])
|
||||
cl = clnt.api.get_configlets_by_device_id(switch['systemMacAddress'])
|
||||
# generate a task if config is out of sync
|
||||
if switch['complianceCode'] in compliance.keys():
|
||||
print(clnt.api.apply_configlets_to_device("", switch, cl))
|
||||
|
||||
else:
|
||||
print("Skipping %s, device is unregistered for provisioning" % switch['hostname'])
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
5
docs/labs/lab07-aaa/aaa_users.csv
Normal file
5
docs/labs/lab07-aaa/aaa_users.csv
Normal file
|
@ -0,0 +1,5 @@
|
|||
username,first_name,last_name,email,user_type,role,status
|
||||
alice,,,alice@abc.xyz,SSO,network-admin,Enabled
|
||||
bob,,,bob@abc.xyz,SSO,network-admin,Enabled
|
||||
jane,Jane,Smith,jane@abc.xyz,SSO,network-admin,Enabled
|
||||
john,John,Smith,john@abc.xyz,SSO,network-admin,Enabled
|
|
32
docs/labs/lab07-aaa/add_new_user_cvaas.py
Normal file
32
docs/labs/lab07-aaa/add_new_user_cvaas.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
from cvprac.cvp_client_errors import CvpApiError
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
from cvprac.cvp_client import CvpClient
|
||||
|
||||
# Create connection to CloudVision using Service Account token
|
||||
with open("cvaas.tok") as f:
|
||||
token = f.read().strip('\n')
|
||||
|
||||
clnt = CvpClient()
|
||||
clnt.connect(nodes=['www.arista.io'], username='', password='', is_cvaas=True, api_token=token)
|
||||
|
||||
username = "john"
|
||||
password = ""
|
||||
role = "network-admin"
|
||||
status = "Enabled"
|
||||
first_name = "John"
|
||||
last_name = "Smith"
|
||||
email = "john.smith@abc.xyz"
|
||||
utype = "SSO"
|
||||
|
||||
try:
|
||||
clnt.api.add_user(username,password,role,status,first_name,last_name,email,utype)
|
||||
except CvpApiError as e:
|
||||
print(e)
|
29
docs/labs/lab07-aaa/add_new_user_onprem.py
Normal file
29
docs/labs/lab07-aaa/add_new_user_onprem.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
from cvprac.cvp_client_errors import CvpApiError
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
from getpass import getpass
|
||||
|
||||
# Create connection to CloudVision
|
||||
clnt = CvpClient()
|
||||
clnt.connect(['cvp1'],'username', 'password')
|
||||
|
||||
username = "cvpuser2"
|
||||
password = getpass()
|
||||
role = "network-admin"
|
||||
status = "Enabled"
|
||||
first_name = "Cloud"
|
||||
last_name = "Vision"
|
||||
email = "cvp@arista.com"
|
||||
utype = "TACACS"
|
||||
|
||||
try:
|
||||
clnt.api.add_user(username,password,role,status,first_name,last_name,email,utype)
|
||||
except CvpApiError as e:
|
||||
print(e)
|
29
docs/labs/lab07-aaa/add_users_from_csv_cvaas.py
Normal file
29
docs/labs/lab07-aaa/add_users_from_csv_cvaas.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
from cvprac.cvp_client_errors import CvpApiError
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import csv
|
||||
|
||||
# Create connection to CloudVision using Service Account token
|
||||
with open("cvaas.tok") as f:
|
||||
token = f.read().strip('\n')
|
||||
|
||||
clnt = CvpClient()
|
||||
clnt.connect(nodes=['www.arista.io'], username='', password='', is_cvaas=True, api_token=token)
|
||||
|
||||
|
||||
with open("aaa_users.csv") as csvfile:
|
||||
for i in csv.DictReader(csvfile):
|
||||
data = dict(i)
|
||||
try:
|
||||
clnt.api.add_user(data['username'], "", data['role'], data['status'], data['first_name'], data['last_name'], data['email'], data['user_type'])
|
||||
except CvpApiError as e:
|
||||
print(e)
|
||||
print ("Adding user {} to CVaaS".format(data['username']))
|
20
docs/labs/lab07-aaa/create_svc_account.py
Normal file
20
docs/labs/lab07-aaa/create_svc_account.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
from cvprac.cvp_client_errors import CvpApiError
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision using user/password (on-prem only)
|
||||
clnt = CvpClient()
|
||||
clnt.connect(['cvp1'],'username', 'password')
|
||||
|
||||
username = "cvprac2"
|
||||
description = "test cvprac"
|
||||
roles = ["network-admin", "clouddeploy"] # both role names and role IDs are supported
|
||||
status = 1 # 1 is equivalent to "ACCOUNT_STATUS_ENABLED"
|
||||
clnt.api.svc_account_set(username, description, roles, status)
|
23
docs/labs/lab07-aaa/create_svc_account_token.py
Normal file
23
docs/labs/lab07-aaa/create_svc_account_token.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
from cvprac.cvp_client_errors import CvpApiError
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision using user/password (on-prem only)
|
||||
clnt = CvpClient()
|
||||
clnt.connect(['cvp1'],'username', 'password')
|
||||
|
||||
username = "cvprac2"
|
||||
duration = "31536000s" # 1 year validity
|
||||
description = "test cvprac"
|
||||
svc_token = clnt.api.svc_account_token_set(username, duration, description)
|
||||
|
||||
# Write the token to file in <username>.tok format
|
||||
with open(svc_token[0]['value']['user'] + ".tok", "w") as f:
|
||||
f.write(svc_token[0]['value']['token'])
|
32
docs/labs/lab07-aaa/create_terminattr_tokens.py
Normal file
32
docs/labs/lab07-aaa/create_terminattr_tokens.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
#
|
||||
# Example script to generate the TerminAttr token via REST API from CVaaS and CV on-prem
|
||||
# and save them to a file
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
from pprint import pprint as pp
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Reading the service account token from a file
|
||||
with open("cvaas.tok") as f:
|
||||
token = f.read().strip('\n')
|
||||
|
||||
clnt = CvpClient()
|
||||
clnt.connect(nodes=['www.arista.io'], username='',password='',is_cvaas=True, api_token=token)
|
||||
|
||||
terminattr_token = clnt.api.create_enroll_token('720h')
|
||||
with open('cv-onboarding-token', 'w') as f:
|
||||
f.write(terminattr_token[0]['enrollmentToken']['token'])
|
||||
|
||||
primary = CvpClient()
|
||||
primary.connect(nodes=['cvp1'], username='username',password='password')
|
||||
|
||||
terminattr_token = primary.api.create_enroll_token('720h')
|
||||
|
||||
with open('token', 'w') as f:
|
||||
f.write(terminattr_token['data'])
|
1
docs/labs/lab07-aaa/cvaas.tok
Normal file
1
docs/labs/lab07-aaa/cvaas.tok
Normal file
|
@ -0,0 +1 @@
|
|||
<copy service account token here>
|
16
docs/labs/lab07-aaa/delete_all_expired_svc_account_tokens.py
Normal file
16
docs/labs/lab07-aaa/delete_all_expired_svc_account_tokens.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
from cvprac.cvp_client_errors import CvpApiError
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision using user/password (on-prem only)
|
||||
clnt = CvpClient()
|
||||
clnt.connect(['cvp1'],'username', 'password')
|
||||
|
||||
clnt.api.svc_account_delete_expired_tokens()
|
17
docs/labs/lab07-aaa/delete_svc_account.py
Normal file
17
docs/labs/lab07-aaa/delete_svc_account.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
from cvprac.cvp_client_errors import CvpApiError
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision using user/password (on-prem only)
|
||||
clnt = CvpClient()
|
||||
clnt.connect(['cvp1'],'username', 'password')
|
||||
|
||||
username = "cvprac2"
|
||||
clnt.api.svc_account_delete(username)
|
22
docs/labs/lab07-aaa/delete_svc_account_created_by_user.py
Normal file
22
docs/labs/lab07-aaa/delete_svc_account_created_by_user.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
from cvprac.cvp_client_errors import CvpApiError
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision using user/password (on-prem only)
|
||||
clnt = CvpClient()
|
||||
clnt.connect(['cvp1'],'username', 'password')
|
||||
|
||||
svc_accounts = clnt.api.svc_account_get_all()
|
||||
created_by = 'john.smith'
|
||||
|
||||
# Delete service accounts created by user john.smith
|
||||
for account in svc_accounts:
|
||||
if account['value']['created_by'] == created_by:
|
||||
clnt.api.svc_account_delete(account['value']['key']['name'])
|
20
docs/labs/lab07-aaa/get_user_info.py
Normal file
20
docs/labs/lab07-aaa/get_user_info.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
from cvprac.cvp_client_errors import CvpApiError
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
from cvprac.cvp_client import CvpClient
|
||||
|
||||
with open("cvaas.tok") as f:
|
||||
token = f.read().strip('\n')
|
||||
|
||||
clnt = CvpClient()
|
||||
clnt.connect(nodes=['www.arista.io'], username='', password='', is_cvaas=True, api_token=token)
|
||||
|
||||
user_info = clnt.api.get_user('kishore')
|
||||
print (user_info)
|
34
docs/labs/lab07-aaa/svc_account_misc.py
Normal file
34
docs/labs/lab07-aaa/svc_account_misc.py
Normal file
|
@ -0,0 +1,34 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
from cvprac.cvp_client_errors import CvpApiError
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Create connection to CloudVision using user/password (on-prem only)
|
||||
clnt = CvpClient()
|
||||
clnt.connect(['cvp1'],'username', 'password')
|
||||
|
||||
# Get all service accounts states
|
||||
|
||||
accounts = clnt.api.svc_account_get_all()
|
||||
|
||||
# Get specific service account state
|
||||
|
||||
account = clnt.api.svc_account_get_one("cvprac2")
|
||||
|
||||
# Get all service account token states
|
||||
|
||||
tokens = clnt.api.svc_account_token_get_all()
|
||||
|
||||
# Get specific token state
|
||||
|
||||
token = clnt.api.svc_account_token_get_one("9bfb39ff892c81d6ac9f25ff95d0389719595feb")
|
||||
|
||||
# Delete a service account token
|
||||
|
||||
clnt.api.svc_account_token_delete("9bfb39ff892c81d6ac9f25ff95d0389719595feb")
|
187
docs/labs/lab08-resource-apis/resource_cvprac.py
Normal file
187
docs/labs/lab08-resource-apis/resource_cvprac.py
Normal file
|
@ -0,0 +1,187 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
from pprint import pprint as pp
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
import requests.packages.urllib3
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
# Reading the service account token from a file
|
||||
with open("token.tok") as f:
|
||||
token = f.read().strip('\n')
|
||||
|
||||
clnt = CvpClient()
|
||||
clnt.connect(nodes=['cvp1'], username='',password='',api_token=token)
|
||||
|
||||
def get_events_all(client):
|
||||
''' Get All events '''
|
||||
event_url = '/api/resources/event/v1/Event/all'
|
||||
response = client.get(event_url)
|
||||
return response['data']
|
||||
|
||||
def get_event(client, key, ts):
|
||||
event_url = '/api/resources/event/v1/Event?'
|
||||
url = event_url + 'key.key=' + key + "&key.timestamp=" + ts
|
||||
response = client.get(url)
|
||||
return response
|
||||
|
||||
def get_events_t1_t2(client, t1, t2):
|
||||
event_url = '/api/resources/event/v1/Event/all?'
|
||||
url = event_url + 'time.start=' + t1 + "&time.end=" + t2
|
||||
response = client.get(url)
|
||||
return response['data']
|
||||
|
||||
def get_events_by_severity(client, severity):
|
||||
payload = {"partialEqFilter": [{"severity": severity }]}
|
||||
event_url = '/api/resources/event/v1/Event/all'
|
||||
response = client.post(event_url, data=payload)
|
||||
if 'data' in response.keys():
|
||||
return response['data']
|
||||
else:
|
||||
return response
|
||||
|
||||
def get_events_by_type(client, etype):
|
||||
payload = {"partialEqFilter": [{"eventType": etype }]}
|
||||
event_url = '/api/resources/event/v1/Event/all'
|
||||
response = client.post(event_url, data=payload)
|
||||
if 'data' in response.keys():
|
||||
return response['data']
|
||||
else:
|
||||
return response
|
||||
|
||||
def get_active_devices(client):
|
||||
''' Get active devices '''
|
||||
dev_url = '/api/resources/inventory/v1/Device/all'
|
||||
devices_data = client.get(dev_url)
|
||||
devices = []
|
||||
for device in devices_data['data']:
|
||||
try:
|
||||
if device['result']['value']['streamingStatus'] == "STREAMING_STATUS_ACTIVE":
|
||||
devices.append(device['result']['value']['hostname'])
|
||||
# pass on archived datasets
|
||||
except KeyError as e:
|
||||
continue
|
||||
return devices
|
||||
|
||||
def get_all_device_tags(client):
|
||||
tag_url = '/api/resources/tag/v1/DeviceTag/all'
|
||||
tag_data = client.get(tag_url)
|
||||
tags = []
|
||||
for tag in tag_data['data']:
|
||||
tags.append({tag['result']['value']['key']['label']:tag['result']['value']['key']['value']})
|
||||
return tags
|
||||
|
||||
def get_all_interface_tags(client):
|
||||
tag_url = '/api/resources/tag/v1/InterfaceTagAssignmentConfig/all'
|
||||
tags = client.get(tag_url)
|
||||
return tags['data']
|
||||
|
||||
def filter_interface_tag(client, dId=None, ifId=None, label=None, value=None):
|
||||
tag_url = '/api/resources/tag/v1/InterfaceTagAssignmentConfig/all'
|
||||
payload = {
|
||||
"partialEqFilter": [
|
||||
{"key": {"deviceId": dId, "interfaceId": ifId, "label": label, "value": value}}
|
||||
]
|
||||
}
|
||||
response = client.post(tag_url, data=payload)
|
||||
return response
|
||||
|
||||
def create_itag(client, label, value):
|
||||
tag_url = '/api/resources/tag/v1/InterfaceTagConfig'
|
||||
payload = {"key":{"label":label,"value":value}}
|
||||
response = client.post(tag_url, data=payload)
|
||||
return response
|
||||
|
||||
def assign_itag(client, dId, ifId, label, value):
|
||||
tag_url = '/api/resources/tag/v1/InterfaceTagAssignmentConfig'
|
||||
payload = {"key":{"label":label, "value":value, "deviceId": dId, "interfaceId": ifId}}
|
||||
response = client.post(tag_url, data=payload)
|
||||
return response
|
||||
|
||||
def create_dtag(client, label, value):
|
||||
tag_url = '/api/resources/tag/v1/DeviceTagConfig'
|
||||
payload = {"key":{"label":label,"value":value}}
|
||||
response = client.post(tag_url, data=payload)
|
||||
return response
|
||||
|
||||
def assign_dtag(client, dId, label, value):
|
||||
tag_url = '/api/resources/tag/v1/DeviceTagAssignmentConfig'
|
||||
payload = {"key":{"label":label, "value":value, "deviceId": dId}}
|
||||
response = client.post(tag_url, data=payload)
|
||||
return response
|
||||
|
||||
### Uncomment the below functions/print statement to test
|
||||
|
||||
# ### Get all active events
|
||||
# print ('=== All active events ===')
|
||||
# cvpevents = get_events_all(clnt)
|
||||
# for event in cvpevents:
|
||||
# print(event)
|
||||
|
||||
# ### Get a specific event
|
||||
# key = "6098ae39e4c8a9d7"
|
||||
# ts ="2021-04-06T21:53:00Z"
|
||||
# get_event(clnt, key, ts)
|
||||
|
||||
# ### Get events between two dates
|
||||
# t1 = "2021-04-06T09:00:00Z"
|
||||
# t2 = "2021-04-06T14:00:00Z"
|
||||
# events = get_events_t1_t2(clnt, t1, t2)
|
||||
# print(f"=== Events between {t1} and {t2} ===")
|
||||
# pp(events)
|
||||
|
||||
# ### Get all INFO severity events ###
|
||||
# # EVENT_SEVERITY_UNSPECIFIED = 0
|
||||
# # EVENT_SEVERITY_INFO = 1
|
||||
# # EVENT_SEVERITY_WARNING = 2
|
||||
# # EVENT_SEVERITY_ERROR = 3
|
||||
# # EVENT_SEVERITY_CRITICAL = 4
|
||||
# ####################################
|
||||
|
||||
# severity = 1 ## Severity INFO
|
||||
# info = get_events_by_severity(clnt, severity)
|
||||
# print('=== Get all INFO severity events ===')
|
||||
# pp(info)
|
||||
|
||||
# ### Get specific event types
|
||||
|
||||
# etype = "LOW_DEVICE_DISK_SPACE"
|
||||
# event = get_events_by_type(clnt, etype)
|
||||
# print('=== Get all Low Disk Space events ===')
|
||||
# pp(event)
|
||||
|
||||
# ### Get the inventory
|
||||
# print ('=== Inventory ===')
|
||||
# print(get_active_devices(clnt))
|
||||
|
||||
# ### Get all devie tags
|
||||
# print('=== Device Tags ===' )
|
||||
# for tag in get_all_device_tags(clnt):
|
||||
# print (tag)
|
||||
|
||||
# ### Get all interface tag assignments
|
||||
# print(get_all_interface_tags(clnt))
|
||||
|
||||
# ### Get all interfaces that have a tag with a specific value on a device
|
||||
# print(filter_interface_tag(clnt, dId="JPE14070534", value="speed40Gbps"))
|
||||
|
||||
# ### Get all tags for an interface of a device
|
||||
# print(filter_interface_tag(clnt, dId="JPE14070534", ifId="Ethernet1"))
|
||||
|
||||
# ### Get all interfaces that have a specific tag assigned
|
||||
# print(filter_interface_tag(clnt, dId="JPE14070534", label="lldp_hostname"))
|
||||
|
||||
# ### Create an interface tag
|
||||
# create_itag(clnt, "lldp_chassis", "50:08:00:0d:00:48")
|
||||
|
||||
# ### Assign an interface tag
|
||||
# assign_itag(clnt, "JPE14070534", "Ethernet4", "lldp_chassis", "50:08:00:0d:00:38")
|
||||
|
||||
# ### Create a device tag
|
||||
# create_dtag(clnt, "topology_hint_pod", "ire-pod11")
|
||||
|
||||
# ### Assign an interface tag
|
||||
# assign_dtag(clnt, "JPE14070534", "topology_hint_pod", "ire-pod11" )
|
106
docs/labs/lab08-resource-apis/topology_tag_assignment.py
Normal file
106
docs/labs/lab08-resource-apis/topology_tag_assignment.py
Normal file
|
@ -0,0 +1,106 @@
|
|||
# Copyright (c) 2021 Arista Networks, Inc.
|
||||
# Use of this source code is governed by the Apache License 2.0
|
||||
# that can be found in the COPYING file.
|
||||
|
||||
# In this example we are going to assign topology tags using the tags.v2 workspace aware API
|
||||
# More details on tag.v2 can be found at https://aristanetworks.github.io/cloudvision-apis/models/tag.v2/
|
||||
# NOTE: Tag.v2 can be used for assigning both device and interface tags (studios, topology, etc) and it's not
|
||||
# limited to topology tags only.
|
||||
# The following are some of the built-in tags that can be used to modify the Topology rendering:
|
||||
# topology_hint_type: < core | edge | endpoint | management | leaf | spine >
|
||||
# topology_hint_rack: < rack name as string >
|
||||
# topology_hint_pod: < pod name as string >
|
||||
# topology_hint_datacenter: < datacenter name as string >
|
||||
# topology_hint_building: < building name as string >
|
||||
# topology_hint_floor: < floor name as string >
|
||||
# topology_network_type: < datacenter | campus | cloud >
|
||||
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
# Reading the service account token from a file
|
||||
with open("token.tok") as f:
|
||||
token = f.read().strip('\n')
|
||||
|
||||
# Create connection to CloudVision
|
||||
clnt = CvpClient()
|
||||
clnt.connect(nodes=['cvp1'], username='',password='',api_token=token)
|
||||
|
||||
tags_common = [{"key": "topology_hint_pod", "value": "tp-avd-pod1"},
|
||||
{"key": "topology_hint_datacenter", "value": "tp-avd-dc1"}]
|
||||
tags_leaf1 = [{"key": "topology_hint_rack", "value": "tp-avd-leafs1"},
|
||||
{"key": "topology_hint_type", "value": "leaf"}]
|
||||
tags_leaf2 = [{"key": "topology_hint_rack", "value": "tp-avd-leafs2"},
|
||||
{"key": "topology_hint_type", "value": "leaf"}]
|
||||
tags_spines = [{"key": "topology_hint_rack", "value": "tp-avd-spines"},
|
||||
{"key": "topology_hint_type", "value": "spine"}]
|
||||
|
||||
# Create workspace
|
||||
display_name = f"Change_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
||||
workspace_id = str(uuid.uuid4())
|
||||
clnt.api.workspace_config(workspace_id,display_name)
|
||||
|
||||
### Create tags
|
||||
element_type = "ELEMENT_TYPE_DEVICE"
|
||||
|
||||
for tag in tags_common+tags_leaf1+tags_leaf2+tags_spines:
|
||||
tag_label = tag['key']
|
||||
tag_value = tag['value']
|
||||
clnt.api.tag_config(element_type, workspace_id, tag_label, tag_value)
|
||||
|
||||
### Assign tags
|
||||
devices = {"leafs1":["BAD032986065E8DC14CBB6472EC314A6","0123F2E4462997EB155B7C50EC148767"],
|
||||
"leafs2":["8520AF39790A4EC959550166DC5DEADE", "6323DA7D2B542B5D09630F87351BEA41"],
|
||||
"spines":["CD0EADBEEA126915EA78E0FB4DC776CA", "2568DB4A33177968A78C4FD5A8232159"]}
|
||||
|
||||
for tag in tags_common+tags_leaf1:
|
||||
tag_label = tag['key']
|
||||
tag_value = tag['value']
|
||||
interface_id = ''
|
||||
for leaf in devices['leafs1']:
|
||||
device_id = leaf
|
||||
clnt.api.tag_assignment_config(element_type, workspace_id, tag_label, tag_value, device_id, interface_id)
|
||||
for tag in tags_common+tags_leaf2:
|
||||
tag_label = tag['key']
|
||||
tag_value = tag['value']
|
||||
interface_id = ''
|
||||
for leaf in devices['leafs2']:
|
||||
device_id = leaf
|
||||
clnt.api.tag_assignment_config(element_type, workspace_id, tag_label, tag_value, device_id, interface_id)
|
||||
for tag in tags_common+tags_spines:
|
||||
tag_label = tag['key']
|
||||
tag_value = tag['value']
|
||||
interface_id = ''
|
||||
for spine in devices['spines']:
|
||||
device_id = spine
|
||||
clnt.api.tag_assignment_config(element_type, workspace_id, tag_label, tag_value, device_id, interface_id)
|
||||
|
||||
### Start build
|
||||
request = 'REQUEST_START_BUILD'
|
||||
request_id = 'b1'
|
||||
description='testing cvprac build'
|
||||
clnt.api.workspace_config(workspace_id=workspace_id, display_name=display_name,
|
||||
description=description, request=request, request_id=request_id)
|
||||
|
||||
### Check workspace build status and proceed only after it finishes building
|
||||
b = 0
|
||||
while b == 0:
|
||||
build_id = request_id
|
||||
# Requesting for the build status too fast might fail if the build start didn't finish creating
|
||||
# the build with the request_id/build_id
|
||||
while True:
|
||||
try:
|
||||
request = clnt.api.workspace_build_status(workspace_id, build_id)
|
||||
break
|
||||
except Exception as e:
|
||||
continue
|
||||
if request['value']['state'] == 'BUILD_STATE_SUCCESS':
|
||||
b = b+1
|
||||
else:
|
||||
continue
|
||||
|
||||
### Submit workspace
|
||||
request = 'REQUEST_SUBMIT'
|
||||
request_id = 's1'
|
||||
clnt.api.workspace_config(workspace_id=workspace_id,display_name=display_name,description=description,request=request,request_id=request_id)
|
BIN
docs/labs/static/serviceaccount1.png
Normal file
BIN
docs/labs/static/serviceaccount1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 115 KiB |
BIN
docs/labs/static/serviceaccount2.png
Normal file
BIN
docs/labs/static/serviceaccount2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 120 KiB |
BIN
docs/labs/static/serviceaccount3.png
Normal file
BIN
docs/labs/static/serviceaccount3.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 104 KiB |
Loading…
Add table
Add a link
Reference in a new issue