Compare commits
2 commits
d2e39936a0
...
661e089729
Author | SHA1 | Date | |
---|---|---|---|
661e089729 | |||
660d60bc9a |
13 changed files with 609 additions and 497 deletions
4
Makefile
4
Makefile
|
@ -49,7 +49,9 @@ coverage_report:
|
|||
|
||||
pep8:
|
||||
-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 cvprac/ test/
|
||||
|
|
40
README.md
40
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
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
1.3.1
|
||||
1.4.0
|
||||
|
|
|
@ -32,5 +32,5 @@
|
|||
''' RESTful API Client class for Cloudvision(R) Portal
|
||||
'''
|
||||
|
||||
__version__ = '1.3.1'
|
||||
__version__ = '1.4.0'
|
||||
__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
|
||||
# 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
|
||||
|
||||
This module provides a RESTful API client for Cloudvision(R) Portal (CVP)
|
||||
|
@ -96,25 +99,31 @@ import json
|
|||
import logging
|
||||
from logging.handlers import SysLogHandler
|
||||
from itertools import cycle
|
||||
from pkg_resources import parse_version
|
||||
from packaging.version import parse
|
||||
|
||||
import requests
|
||||
from requests.exceptions import ConnectionError, HTTPError, Timeout, \
|
||||
ReadTimeout, TooManyRedirects, JSONDecodeError
|
||||
from requests.exceptions import ( # pylint: disable=redefined-builtin
|
||||
ConnectionError,
|
||||
HTTPError,
|
||||
Timeout,
|
||||
ReadTimeout,
|
||||
TooManyRedirects,
|
||||
JSONDecodeError
|
||||
)
|
||||
|
||||
from cvprac.cvp_api import CvpApi
|
||||
from cvprac.cvp_client_errors import CvpApiError, CvpLoginError, \
|
||||
CvpRequestError, CvpSessionLogOutError
|
||||
|
||||
|
||||
class CvpClient(object):
|
||||
class CvpClient():
|
||||
''' Use this class to create a persistent connection to CVP.
|
||||
'''
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
# 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 +221,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,25 +242,37 @@ 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(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.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.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.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.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.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.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.apiversion = 2.0
|
||||
else:
|
||||
|
@ -374,13 +396,12 @@ class CvpClient(object):
|
|||
self.error_msg = '\n'
|
||||
for _ in range(0, num_nodes):
|
||||
host = next(self.node_pool)
|
||||
self.url_prefix = ('https://%s:%d/web' % (host, self.port or 443))
|
||||
self.url_prefix_short = ('https://%s:%d'
|
||||
% (host, self.port or 443))
|
||||
self.url_prefix = f"https://{host}:{self.port or 443}/web"
|
||||
self.url_prefix_short = f"https://{host}:{self.port or 443}"
|
||||
error = self._reset_session()
|
||||
if error is None:
|
||||
break
|
||||
self.error_msg += '%s: %s\n' % (host, error)
|
||||
self.error_msg += f"{host}: {error}\n"
|
||||
|
||||
def _reset_session(self):
|
||||
''' Get a new request session and try logging into the current
|
||||
|
@ -424,23 +445,20 @@ class CvpClient(object):
|
|||
if 'Unauthorized' in response.reason:
|
||||
# Check for 'Unauthorized' User error because this is how
|
||||
# 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)
|
||||
raise CvpApiError(msg)
|
||||
if 'User is unauthorized' in response.text:
|
||||
# Check for 'User is unauthorized' response text because this
|
||||
# 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)
|
||||
raise CvpApiError(msg)
|
||||
else:
|
||||
msg = '%s: Request Error: %s - %s' % (prefix, response.reason,
|
||||
response.text)
|
||||
self.log.error(msg)
|
||||
msg = f"{prefix}: Request Error: {response.reason} - {response.text}"
|
||||
raise CvpRequestError(msg)
|
||||
|
||||
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)
|
||||
|
||||
joutput = json_decoder(response.text)
|
||||
|
@ -456,9 +474,9 @@ class CvpClient(object):
|
|||
# Build the error message from all the errors.
|
||||
err_msg = error_list[0]
|
||||
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)
|
||||
raise CvpApiError(msg)
|
||||
|
||||
|
@ -473,8 +491,7 @@ class CvpClient(object):
|
|||
response status is not OK.
|
||||
'''
|
||||
if not response.ok:
|
||||
msg = '%s: Request Error: %s - %s' % (prefix, response.reason,
|
||||
response.text)
|
||||
msg = f"{prefix}: Request Error: {response.reason} - {response.text}"
|
||||
self.log.error(msg)
|
||||
raise CvpRequestError(msg)
|
||||
|
||||
|
@ -508,7 +525,7 @@ class CvpClient(object):
|
|||
self.headers.pop('APP_SESSION_ID', None)
|
||||
if self.api_token is not None:
|
||||
return self._set_headers_api_token()
|
||||
elif self.is_cvaas:
|
||||
if self.is_cvaas:
|
||||
raise CvpLoginError('CVaaS only supports API token authentication.'
|
||||
' Please create an API token and provide it'
|
||||
' via the api_token parameter in combination'
|
||||
|
@ -547,7 +564,7 @@ class CvpClient(object):
|
|||
headers=self.headers,
|
||||
timeout=self.connect_timeout,
|
||||
verify=self.cert)
|
||||
self._is_good_response(response, 'Authenticate: %s' % url)
|
||||
self._is_good_response(response, f"Authenticate: {url}")
|
||||
|
||||
self.cookies = response.cookies
|
||||
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.
|
||||
# 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
|
||||
# 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, f"Authenticate: {url}")
|
||||
|
||||
def logout(self):
|
||||
'''
|
||||
|
@ -572,7 +599,7 @@ class CvpClient(object):
|
|||
self.log.info('User logged out.')
|
||||
self.session = None
|
||||
else:
|
||||
err = 'Error trying to logout %s' % response
|
||||
err = f"Error trying to logout {response}"
|
||||
self.log.error(err)
|
||||
|
||||
def _make_request(self, req_type, url, timeout, data=None,
|
||||
|
@ -688,8 +715,8 @@ class CvpClient(object):
|
|||
|
||||
try:
|
||||
resp_data = response.json()
|
||||
if (resp_data is not None and 'result' in resp_data
|
||||
and '/resources/' in full_url):
|
||||
if (resp_data is not None and 'result' in resp_data and
|
||||
'/resources/' in full_url):
|
||||
# Resource APIs use JSON streaming and will return
|
||||
# multiple JSON objects during GetAll type API
|
||||
# calls. We are wrapping the multiple objects into
|
||||
|
@ -710,12 +737,10 @@ 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:
|
||||
self.log.error('Unknown format for JSONDecodeError - %s',
|
||||
err_str)
|
||||
self.log.error("Unknown format for JSONDecodeError - %s", err_str)
|
||||
raise error
|
||||
|
||||
def _send_request(self, req_type, full_url, timeout, data=None,
|
||||
|
@ -783,7 +808,7 @@ class CvpClient(object):
|
|||
timeout=timeout,
|
||||
verify=self.cert)
|
||||
else:
|
||||
fhs = dict()
|
||||
fhs = {}
|
||||
fhs['Accept'] = self.headers['Accept']
|
||||
if 'APP_SESSION_ID' in self.headers:
|
||||
fhs['APP_SESSION_ID'] = self.headers[
|
||||
|
@ -818,8 +843,7 @@ class CvpClient(object):
|
|||
continue
|
||||
|
||||
try:
|
||||
self._is_good_response(response, '%s: %s ' %
|
||||
(req_type, full_url))
|
||||
self._is_good_response(response, f"{req_type}: {full_url} ")
|
||||
except CvpSessionLogOutError as error:
|
||||
self.log.debug(error)
|
||||
# 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.
|
||||
if req_try + 1 == self.NUM_RETRY_REQUESTS:
|
||||
raise error
|
||||
else:
|
||||
self._reset_session()
|
||||
if not self.session:
|
||||
raise error
|
||||
|
@ -847,12 +870,10 @@ class CvpClient(object):
|
|||
# will be retried on the same node.
|
||||
if req_try + 1 == self.NUM_RETRY_REQUESTS:
|
||||
raise error
|
||||
else:
|
||||
self._reset_session()
|
||||
if not self.session:
|
||||
raise error
|
||||
continue
|
||||
else:
|
||||
# pylint: disable=raising-bad-type
|
||||
raise error
|
||||
return response
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
check-manifest
|
||||
coverage
|
||||
mock
|
||||
pdoc
|
||||
pep8
|
||||
pyflakes
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import argparse
|
||||
import ssl
|
||||
import sys
|
||||
from pkg_resources import parse_version
|
||||
from packaging.version import parse
|
||||
from getpass import getpass
|
||||
from cvprac.cvp_client import CvpClient
|
||||
import requests.packages.urllib3
|
||||
|
@ -56,7 +56,7 @@ def main():
|
|||
|
||||
# Get the current CVP 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
|
||||
try:
|
||||
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
|
||||
packaging>=23.2
|
||||
|
|
11
setup.py
11
setup.py
|
@ -100,8 +100,13 @@ setup(
|
|||
|
||||
# Specify the Python versions you support here. In particular, ensure
|
||||
# that you indicate whether you support Python 2, Python 3 or both.
|
||||
'Programming Language :: Python :: 2',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Programming Language :: Python :: 3 :: Only',
|
||||
'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?
|
||||
|
@ -111,7 +116,7 @@ setup(
|
|||
# your project is installed. For an analysis of "install_requires" vs pip's
|
||||
# requirements files see:
|
||||
# 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
|
||||
# dependencies). You can install these using the following syntax,
|
||||
|
|
Loading…
Add table
Reference in a new issue