1
0
Fork 0

Merging upstream version 1.3.2+dfsg.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-05 14:18:14 +01:00
parent 77fa26eaf5
commit aa182b4768
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
7 changed files with 127 additions and 29 deletions

View file

@ -4,25 +4,31 @@
## Table of Contents ## Table of Contents
1. [Overview](#overview) - [Arista Cloudvision® Portal RESTful API Client](#arista-cloudvision-portal-restful-api-client)
- [Table of Contents](#table-of-contents)
- [Overview](#overview)
- [Requirements](#requirements) - [Requirements](#requirements)
1. [Installation](#installation) - [Installation](#installation)
- [Development: Run from Source](#development-run-from-source) - [Development: Run from Source](#development-run-from-source)
1. [Getting Started](#getting-started) - [Step 1: Clone the cvprac Github repo](#step-1-clone-the-cvprac-github-repo)
- [Step 2: Check out the desired version or branch](#step-2-check-out-the-desired-version-or-branch)
- [Step 3: Install cvprac using Pip with -e switch](#step-3-install-cvprac-using-pip-with--e-switch)
- [Step 4: Install cvprac development requirements](#step-4-install-cvprac-development-requirements)
- [Getting Started](#getting-started)
- [Connecting](#connecting) - [Connecting](#connecting)
- [CVP On Premises](#cvp-on-premises) - [CVP On Premises](#cvp-on-premises)
- [CVaaS](#cvaas) - [CVaaS](#cvaas)
- [CVP Version Handling](#cvp-version-handling) - [CVP Version Handling](#cvp-version-handling)
- [Examples](#examples) - [Examples](#examples)
1. [Notes For API Class Usage](#notes-for-api-class-usage) - [Notes for API Class Usage](#notes-for-api-class-usage)
- [Containers](#containers) - [Containers](#containers)
1. [Testing](#testing) - [Testing](#testing)
1. [Contact or Questions](#contact-or-questions) - [Contact or Questions](#contact-or-questions)
1. [Contributing](#contributing) - [Contributing](#contributing)
- [Working With Git](#working-with-git) - [Working With Git](#working-with-git)
- [Submitting Pull Requests](#submitting-pull-requests) - [Submitting Pull Requests](#submitting-pull-requests)
- [Pull Request Semantics](#pull-request-semantics) - [Pull Request Semantics](#pull-request-semantics)
1. [License](#license) - [License](#license)
## Overview ## Overview
@ -151,7 +157,7 @@ examples below demonstrate connecting to CVP On Premises setups.
### CVaaS ### CVaaS
CVaaS is CloudVision as a Service. Users with CVaaS must use a REST API CVaaS is CloudVision as a Service. Users with CVaaS must use a REST API
token for accessing CVP with REST APIs. token (service account tokens) for accessing CVP with REST APIs.
- In the case where users authenticate with CVP (CVaaS) using Oauth a - In the case where users authenticate with CVP (CVaaS) using Oauth a
- REST API token is required to be generated and used for running REST - REST API token is required to be generated and used for running REST
@ -170,6 +176,22 @@ generic in this sense. If you are using the cvaas\_token parameter
please convert to api\_token because the cvaas\_token parameter will be please convert to api\_token because the cvaas\_token parameter will be
deprecated in the future. deprecated in the future.
Please note that the correct regional URL where the CVaaS tenant is deployed must be used. The following are the
cluster URLs used in production:
| Region | URL |
|--------|-----|
| United States 1a | [www.arista.io](https://www.arista.io) |
| United States 1c| [www.cv-prod-us-central1-c.arista.io](https://www.cv-prod-us-central1-c.arista.io)|
| Canada | [www.cv-prod-na-northeast1-b.arista.io](https://www.cv-prod-na-northeast1-b.arista.io)|
| Europe West 2| [www.cv-prod-euwest-2.arista.io](https://www.cv-prod-euwest-2.arista.io)|
| Japan| [www.cv-prod-apnortheast-1.arista.io](https://www.cv-prod-apnortheast-1.arista.io)|
| Australia | [www.cv-prod-ausoutheast-1.arista.io](https://www.cv-prod-ausoutheast-1.arista.io)|
!!! Warning
URLs without `www` are not supported.
### CVP Version Handling ### CVP Version Handling
The CVP RESTful APIs often change between releases of CVP. Cvprac The CVP RESTful APIs often change between releases of CVP. Cvprac

View file

@ -1 +1 @@
1.3.1 1.3.2

View file

@ -32,5 +32,5 @@
''' RESTful API Client class for Cloudvision(R) Portal ''' RESTful API Client class for Cloudvision(R) Portal
''' '''
__version__ = '1.3.1' __version__ = '1.3.2'
__author__ = 'Arista Networks, Inc.' __author__ = 'Arista Networks, Inc.'

View file

@ -572,7 +572,7 @@ class CvpApi(object):
'%s&queryparam=&startIndex=%d&endIndex=%d' % '%s&queryparam=&startIndex=%d&endIndex=%d' %
(key, start, end), timeout=self.request_timeout) (key, start, end), timeout=self.request_timeout)
def get_inventory(self, start=0, end=0, query=''): def get_inventory(self, start=0, end=0, query='', provisioned=True):
''' Returns the a dict of the net elements known to CVP. ''' Returns the a dict of the net elements known to CVP.
Args: Args:
@ -595,7 +595,7 @@ class CvpApi(object):
timeout=self.request_timeout) timeout=self.request_timeout)
return data['netElementList'] return data['netElementList']
self.log.debug('v2 Inventory API Call') self.log.debug('v2 Inventory API Call')
data = self.clnt.get('/inventory/devices?provisioned=true', data = self.clnt.get('/inventory/devices?provisioned=%s' % provisioned,
timeout=self.request_timeout) timeout=self.request_timeout)
containers = self.get_containers() containers = self.get_containers()
for dev in data: for dev in data:
@ -1463,7 +1463,7 @@ class CvpApi(object):
return self.clnt.post(url, data=data, timeout=self.request_timeout) return self.clnt.post(url, data=data, timeout=self.request_timeout)
def apply_configlets_to_device(self, app_name, dev, new_configlets, def apply_configlets_to_device(self, app_name, dev, new_configlets,
create_task=True, reorder_configlets=False): create_task=True, reorder_configlets=False, validate=False):
''' Apply the configlets to the device. ''' Apply the configlets to the device.
Args: Args:
@ -1484,6 +1484,12 @@ class CvpApi(object):
directly. Set this parameter to True only with the full directly. Set this parameter to True only with the full
list of configlets being applied to the device provided list of configlets being applied to the device provided
via the new_configlets parameter. via the new_configlets parameter.
validate (bool): Defaults to False. If set to True, the function
will validate and compare the configlets to be attached and
populate the configCompareCount field in the data dict. In case
all keys are 0, ie there is no difference between designed-config
and running-config after applying the configlets, no task will be
generated.
Returns: Returns:
response (dict): A dict that contains a status and a list of response (dict): A dict that contains a status and a list of
@ -1536,6 +1542,16 @@ class CvpApi(object):
'nodeTargetIpAddress': dev['ipAddress'], 'nodeTargetIpAddress': dev['ipAddress'],
'childTasks': [], 'childTasks': [],
'parentTask': ''}]} 'parentTask': ''}]}
if validate:
validation_result = self.validate_configlets_for_device(dev['systemMacAddress'], ckeys)
data['data'][0].update({
"configCompareCount": {
"mismatch": validation_result['mismatch'],
"reconcile": validation_result['reconcile'],
"new": validation_result['new']
}
}
)
self.log.debug('apply_configlets_to_device: saveTopology data:\n%s' % self.log.debug('apply_configlets_to_device: saveTopology data:\n%s' %
data['data']) data['data'])
self._add_temp_action(data) self._add_temp_action(data)
@ -1545,7 +1561,7 @@ class CvpApi(object):
# pylint: disable=too-many-locals # pylint: disable=too-many-locals
def remove_configlets_from_device(self, app_name, dev, del_configlets, def remove_configlets_from_device(self, app_name, dev, del_configlets,
create_task=True): create_task=True, validate=False):
''' Remove the configlets from the device. ''' Remove the configlets from the device.
Args: Args:
@ -1554,6 +1570,12 @@ class CvpApi(object):
del_configlets (list): List of configlet name and key pairs del_configlets (list): List of configlet name and key pairs
create_task (bool): Determines whether or not to execute a save create_task (bool): Determines whether or not to execute a save
and create the tasks (if any) and create the tasks (if any)
validate (bool): Defaults to False. If set to True, the function
will validate and compare the configlets to be attached and
populate the configCompareCount field in the data dict. In case
all keys are 0, ie there is no difference between designed-config
and running-config after applying the configlets, no task will be
generated.
Returns: Returns:
response (dict): A dict that contains a status and a list of response (dict): A dict that contains a status and a list of
@ -1612,6 +1634,16 @@ class CvpApi(object):
'nodeTargetIpAddress': dev['ipAddress'], 'nodeTargetIpAddress': dev['ipAddress'],
'childTasks': [], 'childTasks': [],
'parentTask': ''}]} 'parentTask': ''}]}
if validate:
validation_result = self.validate_configlets_for_device(dev['systemMacAddress'], keep_keys)
data['data'][0].update({
"configCompareCount": {
"mismatch": validation_result['mismatch'],
"reconcile": validation_result['reconcile'],
"new": validation_result['new']
}
}
)
self.log.debug('remove_configlets_from_device: saveTopology data:\n%s' self.log.debug('remove_configlets_from_device: saveTopology data:\n%s'
% data['data']) % data['data'])
self._add_temp_action(data) self._add_temp_action(data)
@ -3775,8 +3807,13 @@ class CvpApi(object):
'deviceId': 'BAD032986065E8DC14CBB6472EC314A6'}, 'deviceId': 'BAD032986065E8DC14CBB6472EC314A6'},
'time': '2022-02-12T02:58:30.765459650Z'} 'time': '2022-02-12T02:58:30.765459650Z'}
''' '''
device_info = self.get_device_by_serial(device_id) device_exists = False
if device_info is not None and 'serialNumber' in device_info: inventory = self.get_inventory(provisioned=False)
for device in inventory:
if device['serialNumber'] == device_id:
device_exists = True
break
if device_exists:
msg = 'Decommissioning via Resource APIs are supported from 2021.3.0 or newer.' msg = 'Decommissioning via Resource APIs are supported from 2021.3.0 or newer.'
# For on-prem check the version as it is only supported from 2021.3.0+ # For on-prem check the version as it is only supported from 2021.3.0+
if self.cvp_version_compare('>=', 7.0, msg): if self.cvp_version_compare('>=', 7.0, msg):

View file

@ -114,7 +114,7 @@ class CvpClient(object):
# Maximum number of times to retry a get or post to the same # Maximum number of times to retry a get or post to the same
# CVP node. # CVP node.
NUM_RETRY_REQUESTS = 3 NUM_RETRY_REQUESTS = 3
LATEST_API_VERSION = 8.0 LATEST_API_VERSION = 9.0
def __init__(self, logger='cvprac', syslog=False, filename=None, def __init__(self, logger='cvprac', syslog=False, filename=None,
log_level='INFO'): log_level='INFO'):
@ -212,7 +212,8 @@ class CvpClient(object):
self.version = version self.version = version
self.log.info('Version %s', version) self.log.info('Version %s', version)
# Set apiversion to latest available API version for CVaaS # Set apiversion to latest available API version for CVaaS
# Set apiversion to 8.0 for 2022.1.x # Set apiversion to 9.0 for 2023.1.x
# Set apiversion to 8.0 for 2022.1.x - 2022.3.x
# Set apiversion to 7.0 for 2021.3.x # Set apiversion to 7.0 for 2021.3.x
# Set apiversion to 6.0 for 2021.2.x # Set apiversion to 6.0 for 2021.2.x
# Set apiversion to 5.0 for 2020.2.4 through 2021.1.x # Set apiversion to 5.0 for 2020.2.4 through 2021.1.x
@ -232,7 +233,10 @@ class CvpClient(object):
' Appending 0. Updated Version String - %s', ' Appending 0. Updated Version String - %s',
".".join(version_components)) ".".join(version_components))
full_version = ".".join(version_components) full_version = ".".join(version_components)
if parse_version(full_version) >= parse_version('2022.1.0'): if parse_version(full_version) >= parse_version('2023.1.0'):
self.log.info('Setting API version to v9')
self.apiversion = 9.0
elif parse_version(full_version) >= parse_version('2022.1.0'):
self.log.info('Setting API version to v8') self.log.info('Setting API version to v8')
self.apiversion = 8.0 self.apiversion = 8.0
elif parse_version(full_version) >= parse_version('2021.3.0'): elif parse_version(full_version) >= parse_version('2021.3.0'):
@ -561,6 +565,14 @@ class CvpClient(object):
# Alternative to adding token to headers it can be added to # Alternative to adding token to headers it can be added to
# cookies as shown below. # cookies as shown below.
# self.cookies = {'access_token': self.api_token} # self.cookies = {'access_token': self.api_token}
url = self.url_prefix_short + '/api/v1/rest/'
response = self.session.get(url,
cookies=self.cookies,
headers=self.headers,
timeout=self.connect_timeout,
verify=self.cert)
# Verify that the generic request was successful
self._is_good_response(response, 'Authenticate: %s' % url)
def logout(self): def logout(self):
''' '''
@ -710,7 +722,7 @@ class CvpClient(object):
err_str) err_str)
if 'Extra data' in str(error): if 'Extra data' in str(error):
self.log.debug('Found multiple objects or NO objects in' self.log.debug('Found multiple objects or NO objects in'
'response data. Attempt to decode') ' response data. Attempt to decode')
decoded_data = json_decoder(response.text) decoded_data = json_decoder(response.text)
return {'data': decoded_data} return {'data': decoded_data}
else: else:

View file

@ -5,10 +5,12 @@ to help users interact with Arista CloudVision easily and automate the provision
## Table of Contents ## Table of Contents
1. [Authentication](#authentication) - [cvprac labs](#cvprac-labs)
- [Table of Contents](#table-of-contents)
- [Authentication](#authentication)
- [Password Authentication](#password-authentication) - [Password Authentication](#password-authentication)
- [Service Account Token Authentication](#service-account-token-authentication) - [Service Account Token Authentication](#service-account-token-authentication)
1. [Known Limitations](#known-limitations) - [Known Limitations](#known-limitations)
## Authentication ## Authentication
@ -60,6 +62,8 @@ clnt = CvpClient()
clnt.connect(nodes=['10.83.13.33'], username='',password='',api_token=token) clnt.connect(nodes=['10.83.13.33'], username='',password='',api_token=token)
``` ```
> Note that for CVaaS the correct regional URL must be used including `www.`. Please refer to the main page's [README.md](../../README.md#cvaas)
## Known Limitations ## Known Limitations
- for any APIs that interact with EOS devices, the service account name must match the name of the username - for any APIs that interact with EOS devices, the service account name must match the name of the username

View file

@ -0,0 +1,23 @@
######
v1.3.2
######
2023-12-14
Enhancements
^^^^^^^^^^^^
* Add handling of new password change logout functionality in 2023.1.0. (`254 <https://github.com/aristanetworks/cvprac/pull/254>`_) [`mharista <https://github.com/mharista>`_]
* Add support for config validation during config assign. (`255 <https://github.com/aristanetworks/cvprac/pull/255>`_) [`noredistribution <https://github.com/noredistribution>`_]
* Add support for config validation during config removal. (`256 <https://github.com/aristanetworks/cvprac/pull/256>`_) [`noredistribution <https://github.com/noredistribution>`_]
Fixed
^^^^^
* Add ability to use device_decommissioning for unprovisioned devices. (`253 <https://github.com/aristanetworks/cvprac/pull/253>`_) [`noredistribution <https://github.com/noredistribution>`_]
* Add check to connect() to ensure token works. (`258 <https://github.com/aristanetworks/cvprac/pull/258>`_) [`chetryan <https://github.com/chetryan>`_]
Documentation
^^^^^^^^^^^^^
* Add documentation for CVaaS regional URLs. (`259 <https://github.com/aristanetworks/cvprac/pull/259>`_) [`noredistribution <https://github.com/noredistribution>`_]