Compare commits
10 commits
73572a54d9
...
53654251b9
Author | SHA1 | Date | |
---|---|---|---|
53654251b9 | |||
952ea9f521 | |||
9652dd6fff | |||
e41c607fb3 | |||
c306a1dcc2 | |||
295cee048d | |||
aa182b4768 | |||
77fa26eaf5 | |||
9df2219874 | |||
e61f06ddc1 |
17 changed files with 648 additions and 501 deletions
4
Makefile
4
Makefile
|
@ -49,7 +49,9 @@ coverage_report:
|
||||||
|
|
||||||
pep8:
|
pep8:
|
||||||
-pep8 -r --max-line-length=120 --ignore=$(PEP8_IGNORE) cvprac/
|
-pep8 -r --max-line-length=120 --ignore=$(PEP8_IGNORE) cvprac/
|
||||||
-pep8 -r --max-line-length=120 --ignore=$(PEP8_IGNORE),E402 test/
|
-pep8 -r --max-line-length=120 --ignore=$(PEP8_IGNORE),E402 test/lib/
|
||||||
|
-pep8 -r --max-line-length=120 --ignore=$(PEP8_IGNORE),E402 test/system/
|
||||||
|
-pep8 -r --ignore=$(PEP8_IGNORE),E402,E501 test/unit/
|
||||||
|
|
||||||
pyflakes:
|
pyflakes:
|
||||||
pyflakes cvprac/ test/
|
pyflakes cvprac/ test/
|
||||||
|
|
40
README.md
40
README.md
|
@ -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
|
||||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
1.3.1
|
1.4.0
|
||||||
|
|
|
@ -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.4.0'
|
||||||
__author__ = 'Arista Networks, Inc.'
|
__author__ = 'Arista Networks, Inc.'
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -29,6 +29,9 @@
|
||||||
# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
||||||
# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
# pylint: disable=too-many-branches,too-many-statements,too-many-locals,too-many-lines
|
||||||
|
|
||||||
''' RESTful API Client class for Cloudvision(R) Portal
|
''' RESTful API Client class for Cloudvision(R) Portal
|
||||||
|
|
||||||
This module provides a RESTful API client for Cloudvision(R) Portal (CVP)
|
This module provides a RESTful API client for Cloudvision(R) Portal (CVP)
|
||||||
|
@ -96,25 +99,31 @@ import json
|
||||||
import logging
|
import logging
|
||||||
from logging.handlers import SysLogHandler
|
from logging.handlers import SysLogHandler
|
||||||
from itertools import cycle
|
from itertools import cycle
|
||||||
from pkg_resources import parse_version
|
from packaging.version import parse
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from requests.exceptions import ConnectionError, HTTPError, Timeout, \
|
from requests.exceptions import ( # pylint: disable=redefined-builtin
|
||||||
ReadTimeout, TooManyRedirects, JSONDecodeError
|
ConnectionError,
|
||||||
|
HTTPError,
|
||||||
|
Timeout,
|
||||||
|
ReadTimeout,
|
||||||
|
TooManyRedirects,
|
||||||
|
JSONDecodeError
|
||||||
|
)
|
||||||
|
|
||||||
from cvprac.cvp_api import CvpApi
|
from cvprac.cvp_api import CvpApi
|
||||||
from cvprac.cvp_client_errors import CvpApiError, CvpLoginError, \
|
from cvprac.cvp_client_errors import CvpApiError, CvpLoginError, \
|
||||||
CvpRequestError, CvpSessionLogOutError
|
CvpRequestError, CvpSessionLogOutError
|
||||||
|
|
||||||
|
|
||||||
class CvpClient(object):
|
class CvpClient():
|
||||||
''' Use this class to create a persistent connection to CVP.
|
''' Use this class to create a persistent connection to CVP.
|
||||||
'''
|
'''
|
||||||
# pylint: disable=too-many-instance-attributes
|
# pylint: disable=too-many-instance-attributes
|
||||||
# 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 +221,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,25 +242,37 @@ 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(full_version) >= parse('2024.1.0'):
|
||||||
|
self.log.info('Setting API version to v12')
|
||||||
|
self.apiversion = 12.0
|
||||||
|
elif parse(full_version) >= parse('2023.3.0'):
|
||||||
|
self.log.info('Setting API version to v11')
|
||||||
|
self.apiversion = 11.0
|
||||||
|
elif parse(full_version) >= parse('2023.2.0'):
|
||||||
|
self.log.info('Setting API version to v10')
|
||||||
|
self.apiversion = 10.0
|
||||||
|
elif parse(full_version) >= parse('2023.1.0'):
|
||||||
|
self.log.info('Setting API version to v9')
|
||||||
|
self.apiversion = 9.0
|
||||||
|
elif parse(full_version) >= parse('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(full_version) >= parse('2021.3.0'):
|
||||||
self.log.info('Setting API version to v7')
|
self.log.info('Setting API version to v7')
|
||||||
self.apiversion = 7.0
|
self.apiversion = 7.0
|
||||||
elif parse_version(full_version) >= parse_version('2021.2.0'):
|
elif parse(full_version) >= parse('2021.2.0'):
|
||||||
self.log.info('Setting API version to v6')
|
self.log.info('Setting API version to v6')
|
||||||
self.apiversion = 6.0
|
self.apiversion = 6.0
|
||||||
elif parse_version(full_version) >= parse_version('2020.2.4'):
|
elif parse(full_version) >= parse('2020.2.4'):
|
||||||
self.log.info('Setting API version to v5')
|
self.log.info('Setting API version to v5')
|
||||||
self.apiversion = 5.0
|
self.apiversion = 5.0
|
||||||
elif parse_version(full_version) >= parse_version('2020.1.1'):
|
elif parse(full_version) >= parse('2020.1.1'):
|
||||||
self.log.info('Setting API version to v4')
|
self.log.info('Setting API version to v4')
|
||||||
self.apiversion = 4.0
|
self.apiversion = 4.0
|
||||||
elif parse_version(full_version) >= parse_version('2019.0.0'):
|
elif parse(full_version) >= parse('2019.0.0'):
|
||||||
self.log.info('Setting API version to v3')
|
self.log.info('Setting API version to v3')
|
||||||
self.apiversion = 3.0
|
self.apiversion = 3.0
|
||||||
elif parse_version(full_version) >= parse_version('2018.2.0'):
|
elif parse(full_version) >= parse('2018.2.0'):
|
||||||
self.log.info('Setting API version to v2')
|
self.log.info('Setting API version to v2')
|
||||||
self.apiversion = 2.0
|
self.apiversion = 2.0
|
||||||
else:
|
else:
|
||||||
|
@ -374,13 +396,12 @@ class CvpClient(object):
|
||||||
self.error_msg = '\n'
|
self.error_msg = '\n'
|
||||||
for _ in range(0, num_nodes):
|
for _ in range(0, num_nodes):
|
||||||
host = next(self.node_pool)
|
host = next(self.node_pool)
|
||||||
self.url_prefix = ('https://%s:%d/web' % (host, self.port or 443))
|
self.url_prefix = f"https://{host}:{self.port or 443}/web"
|
||||||
self.url_prefix_short = ('https://%s:%d'
|
self.url_prefix_short = f"https://{host}:{self.port or 443}"
|
||||||
% (host, self.port or 443))
|
|
||||||
error = self._reset_session()
|
error = self._reset_session()
|
||||||
if error is None:
|
if error is None:
|
||||||
break
|
break
|
||||||
self.error_msg += '%s: %s\n' % (host, error)
|
self.error_msg += f"{host}: {error}\n"
|
||||||
|
|
||||||
def _reset_session(self):
|
def _reset_session(self):
|
||||||
''' Get a new request session and try logging into the current
|
''' Get a new request session and try logging into the current
|
||||||
|
@ -424,23 +445,20 @@ class CvpClient(object):
|
||||||
if 'Unauthorized' in response.reason:
|
if 'Unauthorized' in response.reason:
|
||||||
# Check for 'Unauthorized' User error because this is how
|
# Check for 'Unauthorized' User error because this is how
|
||||||
# CVP responds to a logged out users requests in 2018.x.
|
# CVP responds to a logged out users requests in 2018.x.
|
||||||
msg = '%s: Request Error: %s' % (prefix, response.reason)
|
msg = f"{prefix}: Request Error: {response.reason}"
|
||||||
self.log.error(msg)
|
self.log.error(msg)
|
||||||
raise CvpApiError(msg)
|
raise CvpApiError(msg)
|
||||||
if 'User is unauthorized' in response.text:
|
if 'User is unauthorized' in response.text:
|
||||||
# Check for 'User is unauthorized' response text because this
|
# Check for 'User is unauthorized' response text because this
|
||||||
# is how CVP responds to a logged out users requests in 2019.x.
|
# is how CVP responds to a logged out users requests in 2019.x.
|
||||||
msg = '%s: Request Error: User is unauthorized' % prefix
|
msg = f"{prefix}: Request Error: User is unauthorized"
|
||||||
self.log.error(msg)
|
self.log.error(msg)
|
||||||
raise CvpApiError(msg)
|
raise CvpApiError(msg)
|
||||||
else:
|
msg = f"{prefix}: Request Error: {response.reason} - {response.text}"
|
||||||
msg = '%s: Request Error: %s - %s' % (prefix, response.reason,
|
|
||||||
response.text)
|
|
||||||
self.log.error(msg)
|
|
||||||
raise CvpRequestError(msg)
|
raise CvpRequestError(msg)
|
||||||
|
|
||||||
if 'LOG OUT MESSAGE' in response.text:
|
if 'LOG OUT MESSAGE' in response.text:
|
||||||
msg = ('%s: Request Error: session logged out' % prefix)
|
msg = f"{prefix}: Request Error: session logged out"
|
||||||
raise CvpSessionLogOutError(msg)
|
raise CvpSessionLogOutError(msg)
|
||||||
|
|
||||||
joutput = json_decoder(response.text)
|
joutput = json_decoder(response.text)
|
||||||
|
@ -456,9 +474,9 @@ class CvpClient(object):
|
||||||
# Build the error message from all the errors.
|
# Build the error message from all the errors.
|
||||||
err_msg = error_list[0]
|
err_msg = error_list[0]
|
||||||
for idx in range(1, len(error_list)):
|
for idx in range(1, len(error_list)):
|
||||||
err_msg = '%s\n%s' % (err_msg, error_list[idx])
|
err_msg = f"{err_msg}\n{error_list[idx]}"
|
||||||
|
|
||||||
msg = ('%s: Request Error: %s' % (prefix, err_msg))
|
msg = f"{prefix}: Request Error: {err_msg}"
|
||||||
self.log.error(msg)
|
self.log.error(msg)
|
||||||
raise CvpApiError(msg)
|
raise CvpApiError(msg)
|
||||||
|
|
||||||
|
@ -473,8 +491,7 @@ class CvpClient(object):
|
||||||
response status is not OK.
|
response status is not OK.
|
||||||
'''
|
'''
|
||||||
if not response.ok:
|
if not response.ok:
|
||||||
msg = '%s: Request Error: %s - %s' % (prefix, response.reason,
|
msg = f"{prefix}: Request Error: {response.reason} - {response.text}"
|
||||||
response.text)
|
|
||||||
self.log.error(msg)
|
self.log.error(msg)
|
||||||
raise CvpRequestError(msg)
|
raise CvpRequestError(msg)
|
||||||
|
|
||||||
|
@ -508,7 +525,7 @@ class CvpClient(object):
|
||||||
self.headers.pop('APP_SESSION_ID', None)
|
self.headers.pop('APP_SESSION_ID', None)
|
||||||
if self.api_token is not None:
|
if self.api_token is not None:
|
||||||
return self._set_headers_api_token()
|
return self._set_headers_api_token()
|
||||||
elif self.is_cvaas:
|
if self.is_cvaas:
|
||||||
raise CvpLoginError('CVaaS only supports API token authentication.'
|
raise CvpLoginError('CVaaS only supports API token authentication.'
|
||||||
' Please create an API token and provide it'
|
' Please create an API token and provide it'
|
||||||
' via the api_token parameter in combination'
|
' via the api_token parameter in combination'
|
||||||
|
@ -547,7 +564,7 @@ class CvpClient(object):
|
||||||
headers=self.headers,
|
headers=self.headers,
|
||||||
timeout=self.connect_timeout,
|
timeout=self.connect_timeout,
|
||||||
verify=self.cert)
|
verify=self.cert)
|
||||||
self._is_good_response(response, 'Authenticate: %s' % url)
|
self._is_good_response(response, f"Authenticate: {url}")
|
||||||
|
|
||||||
self.cookies = response.cookies
|
self.cookies = response.cookies
|
||||||
self.headers['APP_SESSION_ID'] = response.json()['sessionId']
|
self.headers['APP_SESSION_ID'] = response.json()['sessionId']
|
||||||
|
@ -557,10 +574,20 @@ class CvpClient(object):
|
||||||
'''
|
'''
|
||||||
# If using an API token there is no need to run a Login API.
|
# If using an API token there is no need to run a Login API.
|
||||||
# Simply add the token into the headers or cookies
|
# Simply add the token into the headers or cookies
|
||||||
self.headers['Authorization'] = 'Bearer %s' % self.api_token
|
self.headers['Authorization'] = f"Bearer {self.api_token}"
|
||||||
# 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, f"Authenticate: {url}")
|
||||||
|
|
||||||
def logout(self):
|
def logout(self):
|
||||||
'''
|
'''
|
||||||
|
@ -572,7 +599,7 @@ class CvpClient(object):
|
||||||
self.log.info('User logged out.')
|
self.log.info('User logged out.')
|
||||||
self.session = None
|
self.session = None
|
||||||
else:
|
else:
|
||||||
err = 'Error trying to logout %s' % response
|
err = f"Error trying to logout {response}"
|
||||||
self.log.error(err)
|
self.log.error(err)
|
||||||
|
|
||||||
def _make_request(self, req_type, url, timeout, data=None,
|
def _make_request(self, req_type, url, timeout, data=None,
|
||||||
|
@ -688,8 +715,8 @@ class CvpClient(object):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
resp_data = response.json()
|
resp_data = response.json()
|
||||||
if (resp_data is not None and 'result' in resp_data
|
if (resp_data is not None and 'result' in resp_data and
|
||||||
and '/resources/' in full_url):
|
'/resources/' in full_url):
|
||||||
# Resource APIs use JSON streaming and will return
|
# Resource APIs use JSON streaming and will return
|
||||||
# multiple JSON objects during GetAll type API
|
# multiple JSON objects during GetAll type API
|
||||||
# calls. We are wrapping the multiple objects into
|
# calls. We are wrapping the multiple objects into
|
||||||
|
@ -710,12 +737,10 @@ 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:
|
self.log.error("Unknown format for JSONDecodeError - %s", err_str)
|
||||||
self.log.error('Unknown format for JSONDecodeError - %s',
|
|
||||||
err_str)
|
|
||||||
raise error
|
raise error
|
||||||
|
|
||||||
def _send_request(self, req_type, full_url, timeout, data=None,
|
def _send_request(self, req_type, full_url, timeout, data=None,
|
||||||
|
@ -783,7 +808,7 @@ class CvpClient(object):
|
||||||
timeout=timeout,
|
timeout=timeout,
|
||||||
verify=self.cert)
|
verify=self.cert)
|
||||||
else:
|
else:
|
||||||
fhs = dict()
|
fhs = {}
|
||||||
fhs['Accept'] = self.headers['Accept']
|
fhs['Accept'] = self.headers['Accept']
|
||||||
if 'APP_SESSION_ID' in self.headers:
|
if 'APP_SESSION_ID' in self.headers:
|
||||||
fhs['APP_SESSION_ID'] = self.headers[
|
fhs['APP_SESSION_ID'] = self.headers[
|
||||||
|
@ -818,8 +843,7 @@ class CvpClient(object):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._is_good_response(response, '%s: %s ' %
|
self._is_good_response(response, f"{req_type}: {full_url} ")
|
||||||
(req_type, full_url))
|
|
||||||
except CvpSessionLogOutError as error:
|
except CvpSessionLogOutError as error:
|
||||||
self.log.debug(error)
|
self.log.debug(error)
|
||||||
# Retry the request to the same node if there was a CVP session
|
# Retry the request to the same node if there was a CVP session
|
||||||
|
@ -828,7 +852,6 @@ class CvpClient(object):
|
||||||
# be retried on the same node.
|
# be retried on the same node.
|
||||||
if req_try + 1 == self.NUM_RETRY_REQUESTS:
|
if req_try + 1 == self.NUM_RETRY_REQUESTS:
|
||||||
raise error
|
raise error
|
||||||
else:
|
|
||||||
self._reset_session()
|
self._reset_session()
|
||||||
if not self.session:
|
if not self.session:
|
||||||
raise error
|
raise error
|
||||||
|
@ -847,12 +870,10 @@ class CvpClient(object):
|
||||||
# will be retried on the same node.
|
# will be retried on the same node.
|
||||||
if req_try + 1 == self.NUM_RETRY_REQUESTS:
|
if req_try + 1 == self.NUM_RETRY_REQUESTS:
|
||||||
raise error
|
raise error
|
||||||
else:
|
|
||||||
self._reset_session()
|
self._reset_session()
|
||||||
if not self.session:
|
if not self.session:
|
||||||
raise error
|
raise error
|
||||||
continue
|
continue
|
||||||
else:
|
|
||||||
# pylint: disable=raising-bad-type
|
# pylint: disable=raising-bad-type
|
||||||
raise error
|
raise error
|
||||||
return response
|
return response
|
||||||
|
|
31
debian/changelog
vendored
31
debian/changelog
vendored
|
@ -1,3 +1,34 @@
|
||||||
|
cvprac (1.4.0+dfsg-1) sid; urgency=medium
|
||||||
|
|
||||||
|
* Uploading to sid.
|
||||||
|
* Merging upstream version 1.4.0+dfsg.
|
||||||
|
* Updating copyright for 2024.
|
||||||
|
* Updating to standards-version 4.7.0.
|
||||||
|
|
||||||
|
-- Daniel Baumann <daniel.baumann@progress-linux.org> Sat, 18 May 2024 07:27:32 +0200
|
||||||
|
|
||||||
|
cvprac (1.3.2+dfsg-1) sid; urgency=medium
|
||||||
|
|
||||||
|
* Uploading to sid.
|
||||||
|
* Merging upstream version 1.3.2+dfsg.
|
||||||
|
|
||||||
|
-- Daniel Baumann <mail@daniel-baumann.ch> Sun, 17 Dec 2023 11:37:55 +0100
|
||||||
|
|
||||||
|
cvprac (1.3.1+dfsg-5) sid; urgency=medium
|
||||||
|
|
||||||
|
* Uploading to sid.
|
||||||
|
* Manually removing some files that pybuild doesn't clean up during
|
||||||
|
build (Closes: #1044183).
|
||||||
|
|
||||||
|
-- Daniel Baumann <daniel.baumann@progress-linux.org> Mon, 14 Aug 2023 10:20:11 +0200
|
||||||
|
|
||||||
|
cvprac (1.3.1+dfsg-4) sid; urgency=medium
|
||||||
|
|
||||||
|
* Uploading to sid.
|
||||||
|
* Adding missing coma in watch file.
|
||||||
|
|
||||||
|
-- Daniel Baumann <daniel.baumann@progress-linux.org> Sat, 24 Jun 2023 19:32:43 +0200
|
||||||
|
|
||||||
cvprac (1.3.1+dfsg-3) sid; urgency=medium
|
cvprac (1.3.1+dfsg-3) sid; urgency=medium
|
||||||
|
|
||||||
* Uploading to sid.
|
* Uploading to sid.
|
||||||
|
|
2
debian/control
vendored
2
debian/control
vendored
|
@ -8,7 +8,7 @@ Build-Depends:
|
||||||
python3-all,
|
python3-all,
|
||||||
python3-setuptools,
|
python3-setuptools,
|
||||||
Rules-Requires-Root: no
|
Rules-Requires-Root: no
|
||||||
Standards-Version: 4.6.2
|
Standards-Version: 4.7.0
|
||||||
Homepage: https://github.com/aristanetworks/cvprac
|
Homepage: https://github.com/aristanetworks/cvprac
|
||||||
Vcs-Browser: https://git.progress-linux.org/users/daniel.baumann/debian/packages/cvprac
|
Vcs-Browser: https://git.progress-linux.org/users/daniel.baumann/debian/packages/cvprac
|
||||||
Vcs-Git: https://git.progress-linux.org/users/daniel.baumann/debian/packages/cvprac
|
Vcs-Git: https://git.progress-linux.org/users/daniel.baumann/debian/packages/cvprac
|
||||||
|
|
6
debian/copyright
vendored
6
debian/copyright
vendored
|
@ -1,16 +1,16 @@
|
||||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||||
Upstream-Name: cvprac
|
Upstream-Name: cvprac
|
||||||
Upstream-Contact: https://github.com/aristanetworks/cvprac/issues
|
Upstream-Contact: https://github.com/aristanetworks/cvprac/issues
|
||||||
Source: https://github.com/aristanetworks/cvprac/releases
|
Source: https://github.com/aristanetworks/cvprac/tags
|
||||||
Files-Excluded:
|
Files-Excluded:
|
||||||
test
|
test
|
||||||
|
|
||||||
Files: *
|
Files: *
|
||||||
Copyright: 2017-2023, Arista Networks, Inc.
|
Copyright: 2017-2024 Arista Networks, Inc.
|
||||||
License: BSD-3-clause
|
License: BSD-3-clause
|
||||||
|
|
||||||
Files: debian/*
|
Files: debian/*
|
||||||
Copyright: 2023 Daniel Baumann <daniel.baumann@progress-linux.org>
|
Copyright: 2023-2024 Daniel Baumann <daniel.baumann@progress-linux.org>
|
||||||
License: BSD-3-clause
|
License: BSD-3-clause
|
||||||
|
|
||||||
License: BSD-3-clause
|
License: BSD-3-clause
|
||||||
|
|
4
debian/rules
vendored
4
debian/rules
vendored
|
@ -5,5 +5,9 @@ export PYBUILD_NAME=cvprac
|
||||||
%:
|
%:
|
||||||
dh ${@} --buildsystem=pybuild
|
dh ${@} --buildsystem=pybuild
|
||||||
|
|
||||||
|
execute_after_dh_auto_clean:
|
||||||
|
# help pybuild
|
||||||
|
rm -rf *.egg-info
|
||||||
|
|
||||||
override_dh_auto_test:
|
override_dh_auto_test:
|
||||||
# disabled
|
# disabled
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
check-manifest
|
check-manifest
|
||||||
coverage
|
coverage
|
||||||
mock
|
|
||||||
pdoc
|
pdoc
|
||||||
pep8
|
pep8
|
||||||
pyflakes
|
pyflakes
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
import argparse
|
import argparse
|
||||||
import ssl
|
import ssl
|
||||||
import sys
|
import sys
|
||||||
from pkg_resources import parse_version
|
from packaging.version import parse
|
||||||
from getpass import getpass
|
from getpass import getpass
|
||||||
from cvprac.cvp_client import CvpClient
|
from cvprac.cvp_client import CvpClient
|
||||||
import requests.packages.urllib3
|
import requests.packages.urllib3
|
||||||
|
@ -56,7 +56,7 @@ def main():
|
||||||
|
|
||||||
# Get the current CVP version
|
# Get the current CVP version
|
||||||
cvp_release = clnt.api.get_cvp_info()['version']
|
cvp_release = clnt.api.get_cvp_info()['version']
|
||||||
if parse_version(cvp_release) < parse_version('2020.3.0'):
|
if parse(cvp_release) < parse('2020.3.0'):
|
||||||
# For older CVP, we manually trigger a compliance check
|
# For older CVP, we manually trigger a compliance check
|
||||||
try:
|
try:
|
||||||
clnt.api.check_compliance('root', 'container')
|
clnt.api.check_compliance('root', 'container')
|
||||||
|
|
23
docs/release-notes-1.3.2.rst
Normal file
23
docs/release-notes-1.3.2.rst
Normal 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>`_]
|
20
docs/release-notes-1.4.0.rst
Normal file
20
docs/release-notes-1.4.0.rst
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
######
|
||||||
|
v1.4.0
|
||||||
|
######
|
||||||
|
|
||||||
|
2024-5-6
|
||||||
|
|
||||||
|
Enhancements
|
||||||
|
^^^^^^^^^^^^
|
||||||
|
|
||||||
|
* Move from pkg_resources to packaging for Python 3.12 support. (`271 <https://github.com/aristanetworks/cvprac/pull/271>`_) [`mharista <https://github.com/mharista>`_]
|
||||||
|
* Add support for searchTopology V3 endpoint. (`275 <https://github.com/aristanetworks/cvprac/pull/275>`_) [`mharista <https://github.com/mharista>`_]
|
||||||
|
|
||||||
|
Fixed
|
||||||
|
^^^^^
|
||||||
|
|
||||||
|
* Add missing url encoding for get_user. (`264 <https://github.com/aristanetworks/cvprac/pull/264>`_) [`noredistribution <https://github.com/noredistribution>`_]
|
||||||
|
* Updated the enrollment endpoint for CVaaS. (`269 <https://github.com/aristanetworks/cvprac/pull/269>`_) [`noredistribution <https://github.com/noredistribution>`_]
|
||||||
|
* Add timeout to get_configlets_and_mappers(). (`270 <https://github.com/aristanetworks/cvprac/pull/270>`_) [`noredistribution <https://github.com/noredistribution>`_]
|
||||||
|
* Update setup.py to reference python3 only. (`272 <https://github.com/aristanetworks/cvprac/pull/272>`_) [`mharista <https://github.com/mharista>`_]
|
||||||
|
* Python3 lint/format fixes. (`273 <https://github.com/aristanetworks/cvprac/pull/273>`_) [`mharista <https://github.com/mharista>`_]
|
|
@ -1 +1,2 @@
|
||||||
requests[socks]>=2.27.0
|
requests[socks]>=2.27.0
|
||||||
|
packaging>=23.2
|
||||||
|
|
11
setup.py
11
setup.py
|
@ -100,8 +100,13 @@ setup(
|
||||||
|
|
||||||
# Specify the Python versions you support here. In particular, ensure
|
# Specify the Python versions you support here. In particular, ensure
|
||||||
# that you indicate whether you support Python 2, Python 3 or both.
|
# that you indicate whether you support Python 2, Python 3 or both.
|
||||||
'Programming Language :: Python :: 2',
|
'Programming Language :: Python :: 3 :: Only',
|
||||||
'Programming Language :: Python :: 2.7',
|
'Programming Language :: Python :: 3.7',
|
||||||
|
'Programming Language :: Python :: 3.8',
|
||||||
|
'Programming Language :: Python :: 3.9',
|
||||||
|
'Programming Language :: Python :: 3.10',
|
||||||
|
'Programming Language :: Python :: 3.11',
|
||||||
|
'Programming Language :: Python :: 3.12',
|
||||||
],
|
],
|
||||||
|
|
||||||
# What does your project relate to?
|
# What does your project relate to?
|
||||||
|
@ -111,7 +116,7 @@ setup(
|
||||||
# your project is installed. For an analysis of "install_requires" vs pip's
|
# your project is installed. For an analysis of "install_requires" vs pip's
|
||||||
# requirements files see:
|
# requirements files see:
|
||||||
# https://packaging.python.org/en/latest/requirements.html
|
# https://packaging.python.org/en/latest/requirements.html
|
||||||
install_requires=['requests[socks]>=2.27.0'],
|
install_requires=['requests[socks]>=2.27.0', 'packaging>=23.2'],
|
||||||
|
|
||||||
# List additional groups of dependencies here (e.g. development
|
# List additional groups of dependencies here (e.g. development
|
||||||
# dependencies). You can install these using the following syntax,
|
# dependencies). You can install these using the following syntax,
|
||||||
|
|
Loading…
Add table
Reference in a new issue