Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -452,13 +452,29 @@ jobs:
--requirement "${requirements}"
"${smoke_python}" -I - "${artifact_kind}" <<'PY'
import importlib
import re
import sys
from importlib.metadata import distribution, version
from importlib.util import find_spec

artifact_kind = sys.argv[1]
if version("scc-firewall-manager-sdk") != "1.17.27":
raise SystemExit("the installed SDK version is not the supported release pin")
sdk_requirements = [
requirement.split(";", 1)[0].strip()
for requirement in distribution("cisco-sccfm-devkit").requires or []
if requirement.split(";", 1)[0].strip().startswith("scc-firewall-manager-sdk")
]
if len(sdk_requirements) != 1:
raise SystemExit("the distribution must declare one SDK dependency")
sdk_requirement = sdk_requirements[0]
sdk_match = re.fullmatch(
r"scc-firewall-manager-sdk\s*(?:\(\s*)?==\s*(?P<version>[^)\s]+)\s*\)?",
sdk_requirement,
)
if sdk_match is None:
raise SystemExit("the distribution must declare an exact SDK release pin")
expected_sdk_version = sdk_match.group("version")
if version("scc-firewall-manager-sdk") != expected_sdk_version:
raise SystemExit("the installed SDK version does not match the release pin")
for package in (
"scc_firewall_manager_sdk",
"cisco_sccfm_cli",
Expand Down
7 changes: 5 additions & 2 deletions cisco_sccfm_core/services/inventory/ftd_deploy_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from scc_firewall_manager_sdk import (
CdoTransaction,
DeviceDeploymentsApi,
FtdDeploymentInput,
FtdMultiDeviceDeploymentInput,
InventoryApi,
Expand All @@ -20,7 +21,9 @@ class FtdDeployService:
"""Deploys configuration changes to cdFMC-managed FTD devices."""

def __init__(self, config: ConfigLike) -> None:
self._inventory_api = InventoryApi(ApiClientFactory().build(config=config))
api_client = ApiClientFactory().build(config=config)
self._inventory_api = InventoryApi(api_client)
self._device_deployments_api = DeviceDeploymentsApi(api_client)

def deploy_single(
self,
Expand Down Expand Up @@ -64,6 +67,6 @@ def deploy_multiple(
description=description,
ignoreWarnings=ignore_warnings,
)
return self._inventory_api.deploy_changes_to_multiple_ftd_devices(
return self._device_deployments_api.deploy_changes_to_multiple_ftd_devices(
ftd_multi_device_deployment_input=deployment_input,
)
36 changes: 28 additions & 8 deletions cisco_sccfm_core/tests/test_ftd_deploy_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,19 @@ def mock_inventory_api(monkeypatch: pytest.MonkeyPatch) -> MagicMock:


@pytest.fixture
def service(mock_inventory_api: MagicMock) -> FtdDeployService:
def mock_device_deployments_api(monkeypatch: pytest.MonkeyPatch) -> MagicMock:
mock_api = MagicMock()
monkeypatch.setattr(
"cisco_sccfm_core.services.inventory.ftd_deploy_service.DeviceDeploymentsApi",
lambda client: mock_api,
)
return mock_api


@pytest.fixture
def service(
mock_inventory_api: MagicMock, mock_device_deployments_api: MagicMock
) -> FtdDeployService:
config = MagicMock()
config.region = "us"
config.api_token = "test-token"
Expand Down Expand Up @@ -85,25 +97,31 @@ def test_should_validate_uid(self, service: FtdDeployService) -> None:

class TestDeployMultiple:
def test_should_call_deploy_multiple_ftd_devices(
self, service: FtdDeployService, mock_inventory_api: MagicMock
self, service: FtdDeployService, mock_device_deployments_api: MagicMock
) -> None:
mock_inventory_api.deploy_changes_to_multiple_ftd_devices.return_value = SAMPLE_TRANSACTION
mock_device_deployments_api.deploy_changes_to_multiple_ftd_devices.return_value = (
SAMPLE_TRANSACTION
)

result = service.deploy_multiple(device_uids=[UID_1, UID_2])

assert result == SAMPLE_TRANSACTION
mock_inventory_api.deploy_changes_to_multiple_ftd_devices.assert_called_once()
call_kwargs = mock_inventory_api.deploy_changes_to_multiple_ftd_devices.call_args[1]
mock_device_deployments_api.deploy_changes_to_multiple_ftd_devices.assert_called_once()
call_kwargs = mock_device_deployments_api.deploy_changes_to_multiple_ftd_devices.call_args[
1
]
deployment_input = call_kwargs["ftd_multi_device_deployment_input"]
assert deployment_input.device_uids == [UID_1, UID_2]
assert deployment_input.deployment_notes is None
assert deployment_input.description is None
assert deployment_input.ignore_warnings is False

def test_should_pass_optional_params(
self, service: FtdDeployService, mock_inventory_api: MagicMock
self, service: FtdDeployService, mock_device_deployments_api: MagicMock
) -> None:
mock_inventory_api.deploy_changes_to_multiple_ftd_devices.return_value = SAMPLE_TRANSACTION
mock_device_deployments_api.deploy_changes_to_multiple_ftd_devices.return_value = (
SAMPLE_TRANSACTION
)

result = service.deploy_multiple(
device_uids=[UID_1, UID_2],
Expand All @@ -113,7 +131,9 @@ def test_should_pass_optional_params(
)

assert result == SAMPLE_TRANSACTION
call_kwargs = mock_inventory_api.deploy_changes_to_multiple_ftd_devices.call_args[1]
call_kwargs = mock_device_deployments_api.deploy_changes_to_multiple_ftd_devices.call_args[
1
]
deployment_input = call_kwargs["ftd_multi_device_deployment_input"]
assert deployment_input.deployment_notes == "Bulk deploy"
assert deployment_input.description == "Weekend maintenance"
Expand Down
13 changes: 11 additions & 2 deletions cisco_sccfm_core/tests/test_packaging_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from __future__ import annotations

import tomllib
from importlib.metadata import version as distribution_version
from pathlib import Path
from typing import Any

Expand Down Expand Up @@ -58,8 +59,16 @@ def test_published_packages_exclude_repository_only_code() -> None:
}


def test_generated_sdk_is_pinned_to_the_verified_compatible_version() -> None:
assert "scc-firewall-manager-sdk==1.17.27" in _project_config()["dependencies"]
def test_generated_sdk_pin_matches_installed_version() -> None:
sdk_dependencies = [
dependency
for dependency in _project_config()["dependencies"]
if dependency.startswith("scc-firewall-manager-sdk==")
]

assert len(sdk_dependencies) == 1
pinned_version = sdk_dependencies[0].split("==", maxsplit=1)[1]
assert distribution_version("scc-firewall-manager-sdk") == pinned_version


def test_interactive_entrypoint_is_published_from_the_cli_package() -> None:
Expand Down
22 changes: 22 additions & 0 deletions cisco_sccfm_core/tests/test_sdk_compatibility.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copyright 2026 Cisco Systems, Inc. and its affiliates
#
# SPDX-License-Identifier: Apache-2.0

"""Regression tests for compatibility with the generated SDK contract."""

from __future__ import annotations

from scc_firewall_manager_sdk.models.device import Device


def test_inventory_device_accepts_unknown_licensing_statuses() -> None:
"""The API uses UNKNOWN when ASA licensing cannot be determined."""
device = Device(
name="asa-1",
deviceType="ASA",
complianceStatus="UNKNOWN",
licenseStatus="UNKNOWN",
)

assert device.compliance_status == "UNKNOWN"
assert device.license_status == "UNKNOWN"
6 changes: 3 additions & 3 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ dependencies = [
"rich>=14.2.0,<15",
"click-option-group>=0.5.9,<0.6",
"questionary>=2.1.1,<3",
"scc-firewall-manager-sdk==1.17.27",
"scc-firewall-manager-sdk==1.22.1573",
"paramiko>=5.0.0,<6",
"cryptography>=50.0.0,<51",
"pygments>=2.20.0,<3",
Expand Down
8 changes: 8 additions & 0 deletions sccfm-ansible/CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ Cisco SCCFM Collection Release Notes

.. contents:: Topics

v0.42.1
========

Bugfixes
--------

- Bumped the SCCFM SDK to 1.22.1573 to support unknown licensing statuses and aligned multi-device FTD deployments with the current SDK API.

v0.42.0
========

Expand Down
7 changes: 7 additions & 0 deletions sccfm-ansible/changelogs/changelog.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@
ancestor: null
# sccfm-release-retarget-seed: 0.39.0
releases:
0.42.1:
changes:
bugfixes:
- Bumped the SCCFM SDK to 1.22.1573 to support unknown licensing
statuses and aligned multi-device FTD deployments with the current SDK API.
fragments: []
release_date: '2026-09-18'
0.42.0:
changes:
minor_changes:
Expand Down
Loading