Skip to content
This repository has been archived by the owner on Nov 21, 2024. It is now read-only.

Commit

Permalink
sync main 0.33.3 (#32)
Browse files Browse the repository at this point in the history
* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Add default None values to the Tier model (#16)

* Fix: admintech wait (#17)

* version check and model update for user

* admin tech - wait for file ready

* get token

* correct logic

* Add default None values to the Tier model (#16)

---------

Co-authored-by: Nikhil <[email protected]>
Co-authored-by: PrzeG <[email protected]>

* fix admintech, generate endpoints md for new release (#18)

* generate endpoints md

* fix admintech api and unittests

* Update README.md

* Update versions_utils.py

```
--------------------------------------------------
nExpress_mtt.test_nExpress_mtt_verify_provider_software_upgrade_data
--------------------------------------------------
verify_provider_software_upgrade_vmanage(PROVIDER)
[PASS] "All VManages updated correctly."

verify_provider_software_upgrade_vbond(PROVIDER)
[PASS] "All VBonds updated correctly."

verify_set_default_partition_for_vbond(PROVIDER)
[PASS] "Successfully set the default version for partition."
```

* Update versions_utils.py

* Update versions_utils.py

* Update versions_utils.py

* Merge branch 'main' into JimOverholt-patch-1

* Merge pull request #20 from cisco-open/JimOverholt-patch-1

Update README.md

* Release 0.33.3

* server_info

* monitoring

* monitoring

* tests

* unit-test

* dev release 0.33.3dev0 prep

---------

Co-authored-by: JimOverholt <[email protected]>
Co-authored-by: PrzeG <[email protected]>
Co-authored-by: Nikhil <[email protected]>
Co-authored-by: Piotr Smialkowski <[email protected]>
Co-authored-by: Jim Overholt <[email protected]>
  • Loading branch information
6 people authored Apr 22, 2024
1 parent 3020907 commit da90d8f
Show file tree
Hide file tree
Showing 16 changed files with 566 additions and 453 deletions.
838 changes: 427 additions & 411 deletions ENDPOINTS.md

Large diffs are not rendered by default.

16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,19 @@

[![Python-Supported](https://img.shields.io/static/v1?label=Python&logo=Python&color=3776AB&message=3.8%20|%203.9%20|%203.10%20|%203.11%20|%203.12)](https://www.python.org/)

Cisco Catalyst WAN SDK is a package for creating simple and parallel automatic requests via official Manager API. It is intended to serve as a multiple session handler (provider, provider as a tenant, tenant). The library is not dependent on environment which is being run in, you just need a connection to any Manager.
Cisco Catalyst WAN SDK is a package for creating simple and parallel automatic requests via official SD-WAN Manager API. It is intended to serve as a multiple session handler (provider, provider as a tenant, tenant). The library is not dependent on environment which is being run in, you just need a connection to any SD-WAN Manager.

## Important Notice: Early Beta Release

Welcome to the Cisco Catalyst WAN SDK!

We are thrilled to announce that Cisco Catalyst WAN SDK is now available in early beta. This is an exciting step forward in enabling developers to harness the full potential of Cisco's networking solutions. Please be aware that, as an early beta release, this version of the SDK is still undergoing development and testing. As such, it is provided "as is" and support to address any issues are limited and best effort.

## Not recommend to use in production environments.
We encourage developers to explore and test the SDK's capabilities, but please exercise caution when using it in production environments. We are dedicated to improving the Cisco Catalyst WAN SDK and we value your input. Your feedback is crucial to us-it will guide us in refining and enhancing the SDK to better meet your needs.
To report any issues, share your insights, or suggest improvements, please visit our Issues page on GitHub or reach out to us through the provided communication channels.

Thank you for being a part of our development journey!

## Installation
```console
Expand Down Expand Up @@ -404,4 +416,4 @@ except ManagerHTTPError as error:

## Seeking support

You can contact us by submitting [issues](https://github.com/cisco-open/cisco-catalyst-wan-sdk/issues), or directly via mail on vmngclient@cisco.com.
You can contact us by submitting [issues](https://github.com/cisco-open/cisco-catalyst-wan-sdk/issues), or directly via mail on catalystwan@cisco.com.
26 changes: 14 additions & 12 deletions catalystwan/api/admin_tech_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,9 @@
from pathlib import Path
from typing import TYPE_CHECKING, List, Optional

from requests import Response
from requests.exceptions import HTTPError

from catalystwan.dataclasses import AdminTech, DeviceAdminTech
from catalystwan.exceptions import CatalystwanException
from catalystwan.exceptions import CatalystwanException, ManagerHTTPError
from catalystwan.response import ManagerResponse
from catalystwan.utils.creation_tools import create_dataclass

if TYPE_CHECKING:
Expand Down Expand Up @@ -117,7 +115,7 @@ def generate(
json=body,
timeout=request_timeout,
)
except HTTPError as http_error:
except ManagerHTTPError as http_error:
response = http_error.response # type: ignore
if response.status_code == 200:
return response.json()["fileName"]
Expand All @@ -131,24 +129,28 @@ def generate(
polling_timer -= polling_interval
raise GenerateAdminTechLogError(f"It is not possible to generate admintech log for {device_id}")

def _get_token_id(self, filename: str) -> str:
admin_techs = self.get_all()
for admin_tech in admin_techs:
if filename == admin_tech.filename:
return admin_tech.token_id
def _get_token_id(self, filename: str, timeout: int, interval: int) -> str:
# Wait for the file to be ready and obtain the token_id
end_time = time.time() + timeout
while time.time() < end_time:
admin_techs = self.get_all()
for admin_tech in admin_techs:
if filename == admin_tech.filename and admin_tech.state == "done":
return admin_tech.token_id
time.sleep(interval)
raise RequestTokenIdNotFound(
f"requestTokenId of admin tech generation request not found for file name: {filename}"
)

def delete(self, filename: str) -> Response:
def delete(self, filename: str, timeout: int = 3600, interval: int = 30) -> ManagerResponse:
"""Deletes admin tech logs for a device.
Args:
filename: name of admin_tech file
Returns:
response: http response for delete operation
"""

token_id = self._get_token_id(filename)
token_id = self._get_token_id(filename, timeout, interval)
response = self.session.delete(f"/dataservice/device/tools/admintech/{token_id}")
if response.status_code == 200:
logger.info(f"Deleted AdminTech file {filename} on remote")
Expand Down
2 changes: 1 addition & 1 deletion catalystwan/api/monitoring_status_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import logging
from typing import TYPE_CHECKING, List

from catalystwan.endpoints.monitoring_status import MonitoringStatus, Status, UpdateStatus
from catalystwan.endpoints.monitoring.status import MonitoringStatus, Status, UpdateStatus
from catalystwan.typed_list import DataSequence

logger = logging.getLogger(__name__)
Expand Down
6 changes: 3 additions & 3 deletions catalystwan/api/templates/payloads/tenant/tenant_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from pydantic import BaseModel, ConfigDict

from catalystwan.api.templates.feature_template import FeatureTemplate
from catalystwan.endpoints.monitoring_device_details import Tier as TierInfo
from catalystwan.endpoints.monitoring.device_details import Tier as TierInfo
from catalystwan.models.tenant import Tenant as TenantInfo

if TYPE_CHECKING:
Expand All @@ -20,8 +20,8 @@ class Tenant(BaseModel):

organization_name: str
tier_name: str
tenant_info: Optional[TenantInfo]
tier_info: Optional[TierInfo]
tenant_info: Optional[TenantInfo] = None
tier_info: Optional[TierInfo] = None


class TenantModel(FeatureTemplate):
Expand Down
6 changes: 4 additions & 2 deletions catalystwan/api/versions_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ def __init__(self, session: ManagerSession):

def _validate_devices_required_fields(self, devices: DataSequence[DeviceDetailsResponse]):
for device in devices:
if not device.uuid or not device.device_ip:
if not device.uuid or not device.local_system_ip:
raise ValueError(
f"Provided device '{device.host_name}' doesn't include required fields for this operation:"
f"device.uuid (current value: {device.uuid})"
Expand Down Expand Up @@ -295,7 +295,9 @@ def _get_devices_chosen_version(

devices_payload = DataSequence(
PartitionDevice,
[PartitionDevice(device_id=device.uuid, device_ip=device.device_ip) for device in devices], # type: ignore
[PartitionDevice(
device_id=device.uuid, device_ip=device.local_system_ip # type: ignore
) for device in devices], # type: ignore
)
all_dev_versions = self.repository.get_devices_versions_repository()
for device in devices_payload:
Expand Down
6 changes: 4 additions & 2 deletions catalystwan/endpoints/endpoints_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,9 @@
from catalystwan.endpoints.configuration_group import ConfigurationGroup
from catalystwan.endpoints.configuration_settings import ConfigurationSettings
from catalystwan.endpoints.misc import MiscellaneousEndpoints
from catalystwan.endpoints.monitoring_device_details import MonitoringDeviceDetails
from catalystwan.endpoints.monitoring_status import MonitoringStatus
from catalystwan.endpoints.monitoring.device_details import MonitoringDeviceDetails
from catalystwan.endpoints.monitoring.server_info import ServerInfo
from catalystwan.endpoints.monitoring.status import MonitoringStatus
from catalystwan.endpoints.real_time_monitoring.reboot_history import RealTimeMonitoringRebootHistory
from catalystwan.endpoints.sdavc_cloud_connector import SDAVCCloudConnector
from catalystwan.endpoints.tenant_backup_restore import TenantBackupRestore
Expand Down Expand Up @@ -200,6 +201,7 @@ def __init__(self, session: ManagerSession):
self.configuration_software_actions = ConfigurationSoftwareActions(session)
self.configuration_disaster_recovery = ConfigurationDisasterRecovery(session)
self.monitoring_device_details = MonitoringDeviceDetails(session)
self.monitoring_server_info = ServerInfo(session)
self.monitoring_status = MonitoringStatus(session)
self.sdavc_cloud_connector = SDAVCCloudConnector(session)
self.tenant_backup_restore = TenantBackupRestore(session)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,28 @@ class Tier(BaseModel):
vpn: int
rid: int = Field(serialization_alias="@rid", validation_alias="@rid")
ipv4_route_limit_type: Optional[str] = Field(
serialization_alias="ipv4RouteLimitType", validation_alias="ipv4RouteLimitType"
default=None, serialization_alias="ipv4RouteLimitType", validation_alias="ipv4RouteLimitType"
)
ipv4_route_limit_threshold: Optional[int] = Field(
serialization_alias="ipv4RouteLimitThreshold", validation_alias="ipv4RouteLimitThreshold"
default=None, serialization_alias="ipv4RouteLimitThreshold", validation_alias="ipv4RouteLimitThreshold"
)
ipv4_route_limit: Optional[int] = Field(
default=None, serialization_alias="ipv4RouteLimit", validation_alias="ipv4RouteLimit"
)
ipv4_route_limit: Optional[int] = Field(serialization_alias="ipv4RouteLimit", validation_alias="ipv4RouteLimit")
ipv6_route_limit_type: Optional[str] = Field(
serialization_alias="ipv6RouteLimitType", validation_alias="ipv6RouteLimitType"
default=None, serialization_alias="ipv6RouteLimitType", validation_alias="ipv6RouteLimitType"
)
ipv6_route_limit_threshold: Optional[int] = Field(
serialization_alias="ipv6RouteLimitThreshold", validation_alias="ipv6RouteLimitThreshold"
default=None, serialization_alias="ipv6RouteLimitThreshold", validation_alias="ipv6RouteLimitThreshold"
)
ipv6_route_limit: Optional[int] = Field(
default=None, serialization_alias="ipv6RouteLimit", validation_alias="ipv6RouteLimit"
)
ipv6_route_limit: Optional[int] = Field(serialization_alias="ipv6RouteLimit", validation_alias="ipv6RouteLimit")
tlocs: List[TLOC] = Field(default=[])
# New in 20.12 version
nat_session_limit: Optional[int] = Field(serialization_alias="natSessionLimit", validation_alias="natSessionLimit")
nat_session_limit: Optional[int] = Field(
default=None, serialization_alias="natSessionLimit", validation_alias="natSessionLimit"
)


class DeviceData(BaseModel):
Expand Down
12 changes: 12 additions & 0 deletions catalystwan/endpoints/monitoring/server_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copyright 2024 Cisco Systems, Inc. and its affiliates

# mypy: disable-error-code="empty-body"

from catalystwan.endpoints import APIEndpoints, get
from catalystwan.models.monitoring.server_info import ServerInfoResponse


class ServerInfo(APIEndpoints):
@get("/server/info")
def get_server_info(self) -> ServerInfoResponse:
...
12 changes: 12 additions & 0 deletions catalystwan/models/monitoring/server_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copyright 2024 Cisco Systems, Inc. and its affiliates

from pydantic import BaseModel, Field


class ServerInfoResponse(BaseModel):
"""The response may contain an incorrect spelling "Achitecture"."""

architecture: str = Field(..., alias="Achitecture")
available_processors: int = Field(
..., serialization_alias="Available processors", validation_alias="Available processors"
)
20 changes: 10 additions & 10 deletions catalystwan/tests/test_admin_tech_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def setUp(self):
self.download_file = io.BytesIO(self.download_file_content.encode())

@patch("catalystwan.session.ManagerSession")
@patch("requests.Response")
@patch("catalystwan.response.ManagerResponse")
def test_get(self, mock_session, mock_response):
# Arrange
mock_session.post.return_value = mock_response
Expand All @@ -78,7 +78,7 @@ def test_get(self, mock_session, mock_response):
self.assertIsInstance(admintechs[0], DeviceAdminTech)

@patch("catalystwan.session.ManagerSession")
@patch("requests.Response")
@patch("catalystwan.response.ManagerResponse")
def test_get_all(self, mock_session, mock_response):
# Arrange
mock_session.get.return_value = mock_response
Expand All @@ -90,7 +90,7 @@ def test_get_all(self, mock_session, mock_response):
self.assertEqual(len(admintechs), len(self.admin_tech_infos["data"]))

@patch("catalystwan.session.ManagerSession")
@patch("requests.Response")
@patch("catalystwan.response.ManagerResponse")
def test_generate(self, mock_session, mock_response):
# Arrange
mock_session.post.return_value = mock_response
Expand All @@ -106,7 +106,7 @@ def test_generate(self, mock_session, mock_response):
self.assertEqual(filename, self.admin_tech_generate_response["fileName"])

@patch("catalystwan.session.ManagerSession")
@patch("requests.Response")
@patch("catalystwan.response.ManagerResponse")
def test_generate_in_progress_error_retry(self, mock_session, mock_response):
# Arrange
mock_session.post.return_value = mock_response
Expand All @@ -124,7 +124,7 @@ def test_generate_in_progress_error_retry(self, mock_session, mock_response):
self.assertEqual(mock_session.post.call_count, count)

@patch("catalystwan.session.ManagerSession")
@patch("requests.Response")
@patch("catalystwan.response.ManagerResponse")
def test_generate_error(self, mock_session, mock_response):
# Arrange
mock_session.post.return_value = mock_response
Expand All @@ -142,7 +142,7 @@ def test_generate_error(self, mock_session, mock_response):
mock_session.post.assert_called_once()

@patch("catalystwan.session.ManagerSession")
@patch("requests.Response")
@patch("catalystwan.response.ManagerResponse")
def test_delete(self, mock_session, mock_response):
# Arrange
filename = self.admin_tech_generate_response["fileName"]
Expand All @@ -155,17 +155,17 @@ def test_delete(self, mock_session, mock_response):
mock_session.delete.assert_called_once_with(f"/dataservice/device/tools/admintech/{token_id}")

@patch("catalystwan.session.ManagerSession")
@patch("requests.Response")
@patch("catalystwan.response.ManagerResponse")
def test_delete_token_not_found(self, mock_session, mock_response):
# Arrange
mock_session.get.return_value = mock_response
mock_response.json.return_value = self.admin_tech_infos
# Act/Assert
with self.assertRaises(RequestTokenIdNotFound):
AdminTechAPI(mock_session).delete("fake-filename.tar.gz")
AdminTechAPI(mock_session).delete("fake-filename.tar.gz", timeout=0.01, interval=0.01)

@patch("catalystwan.session.ManagerSession")
@patch("requests.Response")
@patch("catalystwan.response.ManagerResponse")
def test_download(self, mock_session, mock_response):
# Arrange
filename = self.admin_tech_generate_response["fileName"]
Expand All @@ -180,7 +180,7 @@ def test_download(self, mock_session, mock_response):
self.assertEqual(download_path, Path(tmpdir) / filename)

@patch("catalystwan.session.ManagerSession")
@patch("requests.Response")
@patch("catalystwan.response.ManagerResponse")
def test_download_error(self, mock_session, mock_response):
# Arrange
mock_session.get.return_value = mock_response
Expand Down
2 changes: 1 addition & 1 deletion catalystwan/tests/test_devices_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from catalystwan.api.basic_api import DevicesAPI, DeviceStateAPI
from catalystwan.dataclasses import BfdSessionData, Connection, Device, WanInterface
from catalystwan.endpoints.endpoints_container import APIEndpointContainter
from catalystwan.endpoints.monitoring_device_details import DeviceData
from catalystwan.endpoints.monitoring.device_details import DeviceData
from catalystwan.endpoints.real_time_monitoring.reboot_history import RebootEntry
from catalystwan.exceptions import CatalystwanException
from catalystwan.response import ManagerResponse
Expand Down
49 changes: 49 additions & 0 deletions catalystwan/tests/test_monitoring_server_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Copyright 2022 Cisco Systems, Inc. and its affiliates

import unittest
from unittest.mock import MagicMock, patch

from catalystwan.endpoints.monitoring.server_info import ServerInfo
from catalystwan.models.monitoring.server_info import ServerInfoResponse


class TestServerInfo(unittest.TestCase):
def setUp(self) -> None:
self.mock_server_info_data = {
"Achitecture": "x86_64",
"Available processors": 4,
} # Note the spelling error

@patch("catalystwan.session.ManagerSession")
def test_get_server_info(self, mock_session):
# Arrange
# Create a MagicMock for the response that has a dataobj method
mock_response = MagicMock()
# Set up the mock response's dataobj method to return a ServerInfoResponse
mock_response.dataobj.return_value = ServerInfoResponse(**self.mock_server_info_data)

# Mock the request method of the ManagerSession to return the mock response
mock_session_instance = mock_session.return_value
mock_session_instance.request.return_value = mock_response

server_info_api = ServerInfo(mock_session_instance)

# Act
response = server_info_api.get_server_info()

# Assert
self.assertIsInstance(response, ServerInfoResponse)
self.assertEqual(response.architecture, self.mock_server_info_data["Achitecture"])
self.assertEqual(
response.available_processors,
self.mock_server_info_data["Available processors"],
)
self.assertEqual(response.architecture, "x86_64")
self.assertEqual(response.available_processors, 4)

# Ensure the request method was called
mock_session_instance.request.assert_called_once()


if __name__ == "__main__":
unittest.main()
2 changes: 1 addition & 1 deletion catalystwan/tests/test_monitoring_status_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from unittest.mock import MagicMock, patch

from catalystwan.api.monitoring_status_api import MonitoringStatusAPI
from catalystwan.endpoints.monitoring_status import (
from catalystwan.endpoints.monitoring.status import (
DisabledDeviceListResponse,
EnabledIndexDeviceListResponse,
Status,
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "catalystwan"
version = "0.33.0dev0"
version = "0.33.3dev0"
description = "Cisco Catalyst WAN SDK for Python"
authors = ["kagorski <[email protected]>"]
readme = "README.md"
Expand Down

0 comments on commit da90d8f

Please sign in to comment.