summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--README.md40
-rw-r--r--VERSION2
-rw-r--r--cvprac/__init__.py2
-rw-r--r--cvprac/cvp_api.py61
-rw-r--r--cvprac/cvp_client.py20
-rw-r--r--docs/labs/README.md8
-rw-r--r--docs/release-notes-1.3.2.rst23
7 files changed, 127 insertions, 29 deletions
diff --git a/README.md b/README.md
index bb71888..d74808e 100644
--- a/README.md
+++ b/README.md
@@ -4,25 +4,31 @@
## 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)
-1. [Installation](#installation)
+ - [Installation](#installation)
- [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)
- [CVP On Premises](#cvp-on-premises)
- [CVaaS](#cvaas)
- [CVP Version Handling](#cvp-version-handling)
- [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)
-1. [Testing](#testing)
-1. [Contact or Questions](#contact-or-questions)
-1. [Contributing](#contributing)
+ - [Testing](#testing)
+ - [Contact or Questions](#contact-or-questions)
+ - [Contributing](#contributing)
- [Working With Git](#working-with-git)
- [Submitting Pull Requests](#submitting-pull-requests)
- [Pull Request Semantics](#pull-request-semantics)
-1. [License](#license)
+ - [License](#license)
## Overview
@@ -151,7 +157,7 @@ examples below demonstrate connecting to CVP On Premises setups.
### CVaaS
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
- 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
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
The CVP RESTful APIs often change between releases of CVP. Cvprac
diff --git a/VERSION b/VERSION
index 3a3cd8c..1892b92 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.3.1
+1.3.2
diff --git a/cvprac/__init__.py b/cvprac/__init__.py
index 33ab12d..8d62fa0 100644
--- a/cvprac/__init__.py
+++ b/cvprac/__init__.py
@@ -32,5 +32,5 @@
''' RESTful API Client class for Cloudvision(R) Portal
'''
-__version__ = '1.3.1'
+__version__ = '1.3.2'
__author__ = 'Arista Networks, Inc.'
diff --git a/cvprac/cvp_api.py b/cvprac/cvp_api.py
index dc3fda1..c390989 100644
--- a/cvprac/cvp_api.py
+++ b/cvprac/cvp_api.py
@@ -572,7 +572,7 @@ class CvpApi(object):
'%s&queryparam=&startIndex=%d&endIndex=%d' %
(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.
Args:
@@ -595,7 +595,7 @@ class CvpApi(object):
timeout=self.request_timeout)
return data['netElementList']
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)
containers = self.get_containers()
for dev in data:
@@ -1312,12 +1312,12 @@ class CvpApi(object):
def sanitize_warnings(self, data):
''' Sanitize the warnings returned after validation.
-
+
In some cases where the configlets has both errors
- and warnings, CVP may split any warnings that have
+ and warnings, CVP may split any warnings that have
`,` across multiple strings.
This method concats the strings back into one string
- per warning, and correct the warningCount.
+ per warning, and correct the warningCount.
Args:
data (dict): A dict that contians the result
@@ -1330,11 +1330,11 @@ class CvpApi(object):
# nothing to do here, we can return as is
return data
# Since there may be warnings incorrectly split on
- # ', ' within the warning text by CVP, we join all the
+ # ', ' within the warning text by CVP, we join all the
# warnings together using ', ' into one large string
temp_warnings = ", ".join(data['warnings']).strip()
- # To split the large string again we match on the
+ # To split the large string again we match on the
# 'at line XXX' that should indicate the end of the warning.
# We capture as well the remaining \\n or whitespace and include
# the extra ', ' added in the previous step in the matching criteria.
@@ -1463,7 +1463,7 @@ class CvpApi(object):
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, reorder_configlets=False):
+ create_task=True, reorder_configlets=False, validate=False):
''' Apply the configlets to the device.
Args:
@@ -1484,6 +1484,12 @@ class CvpApi(object):
directly. Set this parameter to True only with the full
list of configlets being applied to the device provided
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:
response (dict): A dict that contains a status and a list of
@@ -1536,6 +1542,16 @@ class CvpApi(object):
'nodeTargetIpAddress': dev['ipAddress'],
'childTasks': [],
'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' %
data['data'])
self._add_temp_action(data)
@@ -1545,7 +1561,7 @@ class CvpApi(object):
# pylint: disable=too-many-locals
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.
Args:
@@ -1554,6 +1570,12 @@ class CvpApi(object):
del_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)
+ 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:
response (dict): A dict that contains a status and a list of
@@ -1612,6 +1634,16 @@ class CvpApi(object):
'nodeTargetIpAddress': dev['ipAddress'],
'childTasks': [],
'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'
% data['data'])
self._add_temp_action(data)
@@ -2952,7 +2984,7 @@ class CvpApi(object):
from_id = parent_cont['key']
else:
from_id = ''
-
+
data = {'data': [{'info': info,
'infoPreview': info,
'action': 'reset',
@@ -3775,8 +3807,13 @@ class CvpApi(object):
'deviceId': 'BAD032986065E8DC14CBB6472EC314A6'},
'time': '2022-02-12T02:58:30.765459650Z'}
'''
- device_info = self.get_device_by_serial(device_id)
- if device_info is not None and 'serialNumber' in device_info:
+ device_exists = False
+ 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.'
# For on-prem check the version as it is only supported from 2021.3.0+
if self.cvp_version_compare('>=', 7.0, msg):
diff --git a/cvprac/cvp_client.py b/cvprac/cvp_client.py
index 0d901b7..602f21a 100644
--- a/cvprac/cvp_client.py
+++ b/cvprac/cvp_client.py
@@ -114,7 +114,7 @@ class CvpClient(object):
# Maximum number of times to retry a get or post to the same
# CVP node.
NUM_RETRY_REQUESTS = 3
- LATEST_API_VERSION = 8.0
+ LATEST_API_VERSION = 9.0
def __init__(self, logger='cvprac', syslog=False, filename=None,
log_level='INFO'):
@@ -212,7 +212,8 @@ class CvpClient(object):
self.version = version
self.log.info('Version %s', version)
# 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 6.0 for 2021.2.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',
".".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.apiversion = 8.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
# cookies as shown below.
# 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):
'''
@@ -710,7 +722,7 @@ class CvpClient(object):
err_str)
if 'Extra data' in str(error):
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)
return {'data': decoded_data}
else:
diff --git a/docs/labs/README.md b/docs/labs/README.md
index 132ee64..d91a81a 100644
--- a/docs/labs/README.md
+++ b/docs/labs/README.md
@@ -5,10 +5,12 @@ to help users interact with Arista CloudVision easily and automate the provision
## Table of Contents
-1. [Authentication](#authentication)
+- [cvprac labs](#cvprac-labs)
+ - [Table of Contents](#table-of-contents)
+ - [Authentication](#authentication)
- [Password Authentication](#password-authentication)
- [Service Account Token Authentication](#service-account-token-authentication)
-1. [Known Limitations](#known-limitations)
+ - [Known Limitations](#known-limitations)
## Authentication
@@ -60,6 +62,8 @@ clnt = CvpClient()
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
- for any APIs that interact with EOS devices, the service account name must match the name of the username
diff --git a/docs/release-notes-1.3.2.rst b/docs/release-notes-1.3.2.rst
new file mode 100644
index 0000000..91958d5
--- /dev/null
+++ b/docs/release-notes-1.3.2.rst
@@ -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>`_]