diff --git a/MANIFEST.in b/MANIFEST.in index 374457c..f9f7ef8 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -6,6 +6,13 @@ include LICENSE include VERSION include .pylintrc recursive-include docs *.rst +recursive-include docs *.cfg +recursive-include docs *.csv +recursive-include docs *.md +recursive-include docs *.png +recursive-include docs *.py +recursive-include docs *.tok +recursive-include docs *.txt recursive-include test *.py recursive-include test *.yaml recursive-include test *.swix diff --git a/VERSION b/VERSION index af0b7dd..238d6e8 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.6 +1.0.7 diff --git a/cvprac/__init__.py b/cvprac/__init__.py index e56ef8a..4828605 100644 --- a/cvprac/__init__.py +++ b/cvprac/__init__.py @@ -32,5 +32,5 @@ ''' RESTful API Client class for Cloudvision(R) Portal ''' -__version__ = '1.0.6' +__version__ = '1.0.7' __author__ = 'Arista Networks, Inc.' diff --git a/cvprac/cvp_api.py b/cvprac/cvp_api.py index 1c42ddd..df1e097 100644 --- a/cvprac/cvp_api.py +++ b/cvprac/cvp_api.py @@ -810,11 +810,15 @@ def get_devices_in_container(self, name): devices.append(device) return devices - def get_device_by_name(self, fqdn): + def get_device_by_name(self, fqdn, search_by_hostname=False): ''' Returns the net element device dict for the devices fqdn name. Args: - fqdn (str): Fully qualified domain name of the device. + fqdn (str): Fully qualified domain name or hostname of the + device. + search_by_hostname (boolean): if set True will attempt to split + the fqdn string to match on the hostname portion + specifically which should be the first component Returns: device (dict): The net element device dict for the device if @@ -826,9 +830,14 @@ def get_device_by_name(self, fqdn): device = {} if 'netElementList' in data: for netelem in data['netElementList']: - if netelem['fqdn'] == fqdn: - device = netelem - break + if not search_by_hostname: + if netelem['fqdn'] == fqdn: + device = netelem + break + else: + if netelem['fqdn'].split('.')[0] == fqdn: + device = netelem + break return device def get_device_by_mac(self, device_mac): @@ -852,6 +861,27 @@ def get_device_by_mac(self, device_mac): break return device + def get_device_by_serial(self, device_serial): + ''' Returns the net element device dict for the devices serial number. + + Args: + device_serial (str): Serial number of the device. + + Returns: + device (dict): The net element device dict for the device if + otherwise returns an empty hash. + ''' + self.log.debug('get_device_by_serial: Serial Number: %s' + % device_serial) + data = self.search_topology(device_serial) + device = {} + if 'netElementList' in data: + for netelem in data['netElementList']: + if netelem['serialNumber'] == device_serial: + device = netelem + break + return device + def get_device_configuration(self, device_mac): ''' Returns the running configuration for the device provided. @@ -1009,21 +1039,24 @@ def add_configlet_builder(self, name, config, draft=False, form=None): draft (bool): If builder is a draft form (list): Array/list of form data Parameters: - fieldId (str): - fieldLabel (str): - value (str): - type (str): { - 'Text box', + fieldId (str): "", + fieldLabel (str): "", + value (str): "", + type (str): "", (Options below) + ('Text box', 'Text area', 'Drop down', 'Check box', 'Radio button', 'IP address', - 'Password' - } - validation: - mandatory (boolean): - helpText (str) + 'Password') + validation: { + mandatory (boolean): true, + }, + helpText (str): "", + depends (str): "", + dataValidationErrorExist (boolean): true, + dataValidation (string): "" Returns: key (str): The key for the configlet @@ -1102,7 +1135,7 @@ def update_configlet(self, config, key, name, wait_task_ids=False): timeout=self.request_timeout) def update_configlet_builder(self, name, key, config, draft=False, - wait_for_task=False): + wait_for_task=False, form=None): ''' Update an existing configlet builder. Args: config (str): Contents of the configlet builder configuration @@ -1110,19 +1143,43 @@ def update_configlet_builder(self, name, key, config, draft=False, name: (str): name of the configlet builder draft (boolean): is update a draft wait_for_task (boolean): wait for task IDs to be generated + form (list): Array/list of form data + Parameters: + fieldId (str): "", + fieldLabel (str): "", + value (str): "", + type (str): "", (Options below) + ('Text box', + 'Text area', + 'Drop down', + 'Check box', + 'Radio button', + 'IP address', + 'Password') + validation: { + mandatory (boolean): true, + }, + helpText (str): "", + depends (str): "", + dataValidationErrorExist (boolean): true, + dataValidation (string): "" ''' + if not form: + form = [] + data = { "name": name, "waitForTaskIds": wait_for_task, "data": { + "formList": form, "main_script": { "data": config } } } debug_str = 'update_configlet_builder:' \ - ' config: {} key: {} name: {} ' - self.log.debug(debug_str.format(config, key, name)) + ' config: {} key: {} name: {} form: {}' + self.log.debug(debug_str.format(config, key, name, form)) # Update the configlet builder url_string = '/configlet/updateConfigletBuilder.do?' \ 'isDraft={}&id={}&action=save' @@ -1262,7 +1319,7 @@ def _save_topology_v2(self, data): return self.clnt.post(url, data=data, timeout=self.request_timeout) def apply_configlets_to_device(self, app_name, dev, new_configlets, - create_task=True): + create_task=True, reorder_configlets=False): ''' Apply the configlets to the device. Args: @@ -1271,6 +1328,18 @@ def apply_configlets_to_device(self, app_name, dev, new_configlets, new_configlets (list): List of configlet name and key pairs create_task (bool): Determines whether or not to execute a save and create the tasks (if any) + reorder_configlets (bool): Defaults to False. To use this + parameter you must first get the full list of configlets + applied to the device (for example via the + get_configlets_by_device_id function) and provide the + full list of configlets (in addition to any new configlets + being applied) in the desired order as the new_configlets + parameter. It is also important to keep in mind configlets + that are applied to parent containers because they will + be applied before configlets applied to the device + directly. Set this parameter to True only with the full + list of configlets being applied to the device provided + via the new_configlets parameter. Returns: response (dict): A dict that contains a status and a list of @@ -1280,15 +1349,17 @@ def apply_configlets_to_device(self, app_name, dev, new_configlets, ''' self.log.debug('apply_configlets_to_device: dev: %s names: %s' % (dev, new_configlets)) - # Get all the configlets assigned to the device. - configlets = self.get_configlets_by_device_id(dev['systemMacAddress']) - # Get a list of the names and keys of the configlets cnames = [] ckeys = [] - for configlet in configlets: - cnames.append(configlet['name']) - ckeys.append(configlet['key']) + + if not reorder_configlets: + # Get all the configlets assigned to the device. + configlets = self.get_configlets_by_device_id( + dev['systemMacAddress']) + for configlet in configlets: + cnames.append(configlet['name']) + ckeys.append(configlet['key']) # Add the new configlets to the end of the arrays for entry in new_configlets: diff --git a/docs/labs/README.md b/docs/labs/README.md new file mode 100644 index 0000000..43652fb --- /dev/null +++ b/docs/labs/README.md @@ -0,0 +1,53 @@ +# 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. + +## 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) + +### User/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: + +![serviceaccount1](./static/serviceaccount1.png) +![serviceaccount2](./static/serviceaccount2.png) +![serviceaccount3](./static/serviceaccount3.png) + +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) +``` \ No newline at end of file diff --git a/docs/labs/lab01-cvp-info/get_cvp_info.py b/docs/labs/lab01-cvp-info/get_cvp_info.py new file mode 100644 index 0000000..0e002d0 --- /dev/null +++ b/docs/labs/lab01-cvp-info/get_cvp_info.py @@ -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) diff --git a/docs/labs/lab02-inventory-operations/compliance_check.py b/docs/labs/lab02-inventory-operations/compliance_check.py new file mode 100644 index 0000000..306b407 --- /dev/null +++ b/docs/labs/lab02-inventory-operations/compliance_check.py @@ -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]) diff --git a/docs/labs/lab02-inventory-operations/remove_all_devices.py b/docs/labs/lab02-inventory-operations/remove_all_devices.py new file mode 100644 index 0000000..e49b804 --- /dev/null +++ b/docs/labs/lab02-inventory-operations/remove_all_devices.py @@ -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(nodes=['cvp1'], username="username",password="password") + +inventory = clnt.api.get_inventory() + +devices = [] +for netelement in inventory: + devices.append(netelement['systemMacAddress']) + +clnt.api.delete_devices(devices) diff --git a/docs/labs/lab02-inventory-operations/remove_devices.py b/docs/labs/lab02-inventory-operations/remove_devices.py new file mode 100644 index 0000000..3cde0b3 --- /dev/null +++ b/docs/labs/lab02-inventory-operations/remove_devices.py @@ -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 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"] + +clnt.api.delete_devices(devices) diff --git a/docs/labs/lab02-inventory-operations/remove_devices_from_container.py b/docs/labs/lab02-inventory-operations/remove_devices_from_container.py new file mode 100644 index 0000000..c432fe8 --- /dev/null +++ b/docs/labs/lab02-inventory-operations/remove_devices_from_container.py @@ -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 +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") + +devices = [] +for netelement in inventory: + devices.append(netelement['systemMacAddress']) + +clnt.api.delete_devices(devices) diff --git a/docs/labs/lab03-configlet-management/assign_configlet_to_device.py b/docs/labs/lab03-configlet-management/assign_configlet_to_device.py new file mode 100644 index 0000000..2973408 --- /dev/null +++ b/docs/labs/lab03-configlet-management/assign_configlet_to_device.py @@ -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]) diff --git a/docs/labs/lab03-configlet-management/backup_configlets.py b/docs/labs/lab03-configlet-management/backup_configlets.py new file mode 100644 index 0000000..6543a6b --- /dev/null +++ b/docs/labs/lab03-configlet-management/backup_configlets.py @@ -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']) diff --git a/docs/labs/lab03-configlet-management/backup_configletsV2.py b/docs/labs/lab03-configlet-management/backup_configletsV2.py new file mode 100644 index 0000000..8dff0b2 --- /dev/null +++ b/docs/labs/lab03-configlet-management/backup_configletsV2.py @@ -0,0 +1,47 @@ +# 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 configlet in results[0]['data']: + with open(configlet['name'],'w') as f: + f.write(configlet['config']) diff --git a/docs/labs/lab03-configlet-management/common.cfg b/docs/labs/lab03-configlet-management/common.cfg new file mode 100644 index 0000000..7620ae1 --- /dev/null +++ b/docs/labs/lab03-configlet-management/common.cfg @@ -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 +! diff --git a/docs/labs/lab03-configlet-management/configlet_list.txt b/docs/labs/lab03-configlet-management/configlet_list.txt new file mode 100644 index 0000000..645252c --- /dev/null +++ b/docs/labs/lab03-configlet-management/configlet_list.txt @@ -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 \ No newline at end of file diff --git a/docs/labs/lab03-configlet-management/create_configlet.py b/docs/labs/lab03-configlet-management/create_configlet.py new file mode 100644 index 0000000..8579fbf --- /dev/null +++ b/docs/labs/lab03-configlet-management/create_configlet.py @@ -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) diff --git a/docs/labs/lab03-configlet-management/create_configlet_from_file.py b/docs/labs/lab03-configlet-management/create_configlet_from_file.py new file mode 100644 index 0000000..d6d07c6 --- /dev/null +++ b/docs/labs/lab03-configlet-management/create_configlet_from_file.py @@ -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) diff --git a/docs/labs/lab03-configlet-management/get_configlets.py b/docs/labs/lab03-configlet-management/get_configlets.py new file mode 100644 index 0000000..fc3dc2d --- /dev/null +++ b/docs/labs/lab03-configlet-management/get_configlets.py @@ -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) diff --git a/docs/labs/lab03-configlet-management/reorder_configlet_on_device.py b/docs/labs/lab03-configlet-management/reorder_configlet_on_device.py new file mode 100644 index 0000000..3a5bb6f --- /dev/null +++ b/docs/labs/lab03-configlet-management/reorder_configlet_on_device.py @@ -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) diff --git a/docs/labs/lab03-configlet-management/update_configlet.py b/docs/labs/lab03-configlet-management/update_configlet.py new file mode 100644 index 0000000..b3eb9c0 --- /dev/null +++ b/docs/labs/lab03-configlet-management/update_configlet.py @@ -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) diff --git a/docs/labs/lab04-container-management/add_image_to_container.py b/docs/labs/lab04-container-management/add_image_to_container.py new file mode 100644 index 0000000..99fc05e --- /dev/null +++ b/docs/labs/lab04-container-management/add_image_to_container.py @@ -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) diff --git a/docs/labs/lab04-container-management/assign_configlet_to_container.py b/docs/labs/lab04-container-management/assign_configlet_to_container.py new file mode 100644 index 0000000..a6e8828 --- /dev/null +++ b/docs/labs/lab04-container-management/assign_configlet_to_container.py @@ -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]) diff --git a/docs/labs/lab04-container-management/create_container.py b/docs/labs/lab04-container-management/create_container.py new file mode 100644 index 0000000..6e5b8dc --- /dev/null +++ b/docs/labs/lab04-container-management/create_container.py @@ -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"]) diff --git a/docs/labs/lab04-container-management/remove_image_from_container.py b/docs/labs/lab04-container-management/remove_image_from_container.py new file mode 100644 index 0000000..8a48be8 --- /dev/null +++ b/docs/labs/lab04-container-management/remove_image_from_container.py @@ -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) diff --git a/docs/labs/lab04-container-management/rename_container.py b/docs/labs/lab04-container-management/rename_container.py new file mode 100644 index 0000000..74f4562 --- /dev/null +++ b/docs/labs/lab04-container-management/rename_container.py @@ -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([]) diff --git a/docs/labs/lab05-device-management/add_image_to_devices.py b/docs/labs/lab05-device-management/add_image_to_devices.py new file mode 100644 index 0000000..9150a3c --- /dev/null +++ b/docs/labs/lab05-device-management/add_image_to_devices.py @@ -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) diff --git a/docs/labs/lab05-device-management/add_image_wo_tempaction.py b/docs/labs/lab05-device-management/add_image_wo_tempaction.py new file mode 100644 index 0000000..1100a5a --- /dev/null +++ b/docs/labs/lab05-device-management/add_image_wo_tempaction.py @@ -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) \ No newline at end of file diff --git a/docs/labs/lab05-device-management/remove_image_from_device.py b/docs/labs/lab05-device-management/remove_image_from_device.py new file mode 100644 index 0000000..5e9910c --- /dev/null +++ b/docs/labs/lab05-device-management/remove_image_from_device.py @@ -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) diff --git a/docs/labs/lab05-device-management/set_mgmt_ip.py b/docs/labs/lab05-device-management/set_mgmt_ip.py new file mode 100644 index 0000000..f0c33f1 --- /dev/null +++ b/docs/labs/lab05-device-management/set_mgmt_ip.py @@ -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":" Device IP Address Changed to 10.83.13.214", + "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([]) diff --git a/docs/labs/lab06-provisioning/change_control_workflow.py b/docs/labs/lab06-provisioning/change_control_workflow.py new file mode 100644 index 0000000..af1ee4b --- /dev/null +++ b/docs/labs/lab06-provisioning/change_control_workflow.py @@ -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() +from datetime import datetime + +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']) diff --git a/docs/labs/lab06-provisioning/gen_builder.py b/docs/labs/lab06-provisioning/gen_builder.py new file mode 100644 index 0000000..8cd389d --- /dev/null +++ b/docs/labs/lab06-provisioning/gen_builder.py @@ -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":"Configlet Assign: 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([]) diff --git a/docs/labs/lab06-provisioning/move_device.py b/docs/labs/lab06-provisioning/move_device.py new file mode 100644 index 0000000..5257f79 --- /dev/null +++ b/docs/labs/lab06-provisioning/move_device.py @@ -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) diff --git a/docs/labs/lab06-provisioning/vc_task_retrigger.py b/docs/labs/lab06-provisioning/vc_task_retrigger.py new file mode 100644 index 0000000..512adaa --- /dev/null +++ b/docs/labs/lab06-provisioning/vc_task_retrigger.py @@ -0,0 +1,42 @@ +# 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 + +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") + +# Trigger tasks after they were cancelled + +devices = ["tp-avd-leaf1", "tp-avd-leaf2", "tp-avd-leaf3", "tp-avd-leaf4", "tp-avd-spine1", "tp-avd-spine2"] + +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", +} + +for dev in devices: + # generate device object + device = clnt.api.get_device_by_name(dev) + # generate configlet list + cl = clnt.api.get_configlets_by_device_id(device['systemMacAddress']) + # generate a task if config is out of sync + if device['complianceCode'] in compliance.keys(): + print(clnt.api.apply_configlets_to_device("", device, cl)) diff --git a/docs/labs/lab07-aaa/aaa_users.csv b/docs/labs/lab07-aaa/aaa_users.csv new file mode 100644 index 0000000..14b3706 --- /dev/null +++ b/docs/labs/lab07-aaa/aaa_users.csv @@ -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 \ No newline at end of file diff --git a/docs/labs/lab07-aaa/add_new_user_cvaas.py b/docs/labs/lab07-aaa/add_new_user_cvaas.py new file mode 100644 index 0000000..af2d48e --- /dev/null +++ b/docs/labs/lab07-aaa/add_new_user_cvaas.py @@ -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) diff --git a/docs/labs/lab07-aaa/add_new_user_onprem.py b/docs/labs/lab07-aaa/add_new_user_onprem.py new file mode 100644 index 0000000..218c9fc --- /dev/null +++ b/docs/labs/lab07-aaa/add_new_user_onprem.py @@ -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) diff --git a/docs/labs/lab07-aaa/add_users_from_csv_cvaas.py b/docs/labs/lab07-aaa/add_users_from_csv_cvaas.py new file mode 100644 index 0000000..c5cdda5 --- /dev/null +++ b/docs/labs/lab07-aaa/add_users_from_csv_cvaas.py @@ -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'])) diff --git a/docs/labs/lab07-aaa/cvaas.tok b/docs/labs/lab07-aaa/cvaas.tok new file mode 100644 index 0000000..9d0234c --- /dev/null +++ b/docs/labs/lab07-aaa/cvaas.tok @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/labs/lab07-aaa/get_user_info.py b/docs/labs/lab07-aaa/get_user_info.py new file mode 100644 index 0000000..5e5a193 --- /dev/null +++ b/docs/labs/lab07-aaa/get_user_info.py @@ -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) diff --git a/docs/labs/lab08-resource-apis/resource_cvprac.py b/docs/labs/lab08-resource-apis/resource_cvprac.py new file mode 100644 index 0000000..e454fc9 --- /dev/null +++ b/docs/labs/lab08-resource-apis/resource_cvprac.py @@ -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" ) diff --git a/docs/labs/static/serviceaccount1.png b/docs/labs/static/serviceaccount1.png new file mode 100644 index 0000000..b6de68b Binary files /dev/null and b/docs/labs/static/serviceaccount1.png differ diff --git a/docs/labs/static/serviceaccount2.png b/docs/labs/static/serviceaccount2.png new file mode 100644 index 0000000..3b49542 Binary files /dev/null and b/docs/labs/static/serviceaccount2.png differ diff --git a/docs/labs/static/serviceaccount3.png b/docs/labs/static/serviceaccount3.png new file mode 100644 index 0000000..2eca99a Binary files /dev/null and b/docs/labs/static/serviceaccount3.png differ diff --git a/docs/release-notes-1.0.6.rst b/docs/release-notes-1.0.6.rst index 170647f..de52e0a 100644 --- a/docs/release-notes-1.0.6.rst +++ b/docs/release-notes-1.0.6.rst @@ -7,7 +7,7 @@ v1.0.6 New Modules ^^^^^^^^^^^ -* Started to add api method update_configlet_builder and add test.. (`a32dd7a `_) [`dbm79 `_] +* Added API method update_configlet_builder and test. (`a32dd7a `_) [`dbm79 `_] * Added function and test for API endpoint updateReconcileConfiglet.do. (`7e90de9 `_) [`mharista `_] Enhancements @@ -19,7 +19,7 @@ Fixed ^^^^^ * Fix client logout function to use cvprac client post function instead of session post function. (`abaf257 `_) [`mharista `_] -* Mask localhost/127.0.0.1 with node ip for cb scripts. (`d45ac6e `_) [`Rajat Bajaj `_] +* Mask localhost/127.0.0.1 with node IP for configlet builder scripts. (`d45ac6e `_) [`Rajat Bajaj `_] * Updating info string to tackle backend inconsistent state when moving devices from the Undefined container. (`82ea8b9 `_) [`noredistribution `_] * Remove CVaaS un/pw login. Only API tokens for CVaaS now. (`f9fd6b5 `_) [`mharista `_] * Update redundant functions to self reference. (`0095b00 `_) [`mharista `_] diff --git a/docs/release-notes-1.0.7.rst b/docs/release-notes-1.0.7.rst new file mode 100644 index 0000000..137ad10 --- /dev/null +++ b/docs/release-notes-1.0.7.rst @@ -0,0 +1,19 @@ +###### +v1.0.7 +###### + +2021-7-1 + +New Modules +^^^^^^^^^^^ + +* Added new method for searching for a device by serial_number. (`f23e154 `_) [`titom73 `_] + +Enhancements +^^^^^^^^^^^^ + +* Added form parameter to update_configlet_builder function. (`e5e3719 `_) [`mharista `_] +* Added parameter to apply_configlets_to_device for reordering existing configlets. (`6681800 `_) [`mharista `_] +* Added documentation examples of cvprac usage in docs/labs. (`b1c3443 `_) [`noredistribution `_] +* Added support for searching by hostname to get_device_by_name. (`1eb08cb `_) [`titom73 `_] +* Added lab example for saving topology with specific tempaction data. (`f3282a2 `_) [`noredistribution `_] diff --git a/test/system/test_cvp_api.py b/test/system/test_cvp_api.py index 5a53e9f..0f57b34 100644 --- a/test/system/test_cvp_api.py +++ b/test/system/test_cvp_api.py @@ -694,6 +694,20 @@ def test_api_get_device_by_name_substring(self): self.assertIsNotNone(result) self.assertEqual(result, {}) + def test_api_get_device_by_name_search_by_hostname(self): + ''' Verify get_device_by_name with hostname portion of fqdn is + successful with search_by_hostname parameter set to True + ''' + if 'hostname' in self.device: + hostname = self.device['hostname'] + else: + hostname = self.device['hostname'].split('.')[0] + result = self.api.get_device_by_name(hostname, + search_by_hostname=True) + self.assertIsNotNone(result) + self.assertEqual(result['fqdn'], self.device['fqdn']) + self.assertEqual(result['fqdn'].split('.')[0], hostname) + def test_api_get_device_by_mac(self): ''' Verify get_device_by_mac with partial fqdn returns nothing ''' @@ -709,6 +723,20 @@ def test_api_get_device_by_mac_bad(self): self.assertIsNotNone(result) self.assertEqual(result, {}) + def test_api_get_device_by_serial(self): + ''' Verify get_device_by_serial + ''' + result = self.api.get_device_by_serial(self.device['serialNumber']) + self.assertIsNotNone(result) + self.assertEqual(result['serialNumber'], self.device['serialNumber']) + + def test_api_get_device_by_serial_bad(self): + ''' Verify get_device_by_mac with bad serial + ''' + result = self.api.get_device_by_serial('bogus_serial') + self.assertIsNotNone(result) + self.assertEqual(result, {}) + def _create_configlet_builder(self, name, config, draft, form=None): # Delete configlet builder in case it was left by a previous test run try: @@ -1137,8 +1165,10 @@ def test_api_container_url_encode_name(self): def test_api_configlets_to_device(self): ''' Verify apply_configlets_to_device and - remove_configlets_from_device + remove_configlets_from_device. Also test apply_configlets_to_device + with reorder_configlets parameter set to True ''' + # pylint: disable=too-many-statements # Create a new configlet name = 'test_device_configlet' config = 'alias srie show running-config interface ethernet 1' @@ -1170,10 +1200,50 @@ def test_api_configlets_to_device(self): # Execute Task self._execute_long_running_task(task_id) + # Get current configlets order with new configlet before reordering + configlets_order = self.api.get_configlets_by_device_id( + self.device['systemMacAddress']) + # Swap order of last two configlets + last_configlet = configlets_order[-1] + second_to_last = configlets_order[-2] + configlets_order[-1] = second_to_last + configlets_order[-2] = last_configlet + # Get the next task ID task_id = self._get_next_task_id() - # Remove configlet from device + # reorder configlets + self.api.apply_configlets_to_device(label, self.device, + configlets_order, create_task=True, + reorder_configlets=True) + + # Validate task was created to remove the configlet to device + # Wait 30 seconds for task to get created + cnt = 30 + while cnt > 0: + time.sleep(1) + result = self.api.get_task_by_id(task_id) + if result is not None: + break + cnt -= 1 + self.assertIsNotNone(result) + self.assertEqual(result['workOrderId'], task_id) + self.assertIn(label, result['description']) + + # Execute Task + self._execute_long_running_task(task_id) + + # Get reordered configlets + configlets_order = self.api.get_configlets_by_device_id( + self.device['systemMacAddress']) + # Verify order of last two configlets swapped + self.assertEqual(configlets_order[-1]['name'], second_to_last['name']) + self.assertEqual(configlets_order[-2]['name'], last_configlet['name']) + + # Get the next task ID + task_id = self._get_next_task_id() + + # Remove new configlet from device self.api.remove_configlets_from_device(label, self.device, [param]) # Validate task was created to remove the configlet to device @@ -1192,7 +1262,7 @@ def test_api_configlets_to_device(self): # Execute Task self._execute_long_running_task(task_id) - # Delete the configlet + # Delete the new configlet self.api.delete_configlet(name, key) # Check compliance