from datetime import datetime from typing import List from unittest import SkipTest import boto3 import pytest from botocore.exceptions import ClientError from dateutil.tz import tzutc from freezegun import freeze_time from moto import mock_aws, settings from moto.moto_api import state_manager @mock_aws def test_provision_device() -> None: if settings.TEST_SERVER_MODE: raise SkipTest("Can't use ManagedState in ServerMode") client = boto3.client("panorama", region_name="eu-west-1") given_device_name = "test-device-name" state_manager.set_transition( model_name=f"panorama::device_{given_device_name}_provisioning_status", transition={"progression": "manual", "times": 1}, ) resp = client.provision_device( Description=given_device_name, Name="test-device-name", NetworkingConfiguration={ "Ethernet0": { "ConnectionType": "STATIC_IP", "StaticIpConnectionInfo": { "DefaultGateway": "192.168.1.1", "Dns": [ "8.8.8.8", ], "IpAddress": "192.168.1.10", "Mask": "255.255.255.0", }, }, "Ethernet1": { "ConnectionType": "dhcp", }, "Ntp": { "NtpServers": [ "0.pool.ntp.org", "1.pool.ntp.org", "0.fr.pool.ntp.org", ] }, }, Tags={"Key": "test-key", "Value": "test-value"}, ) assert ( resp["Arn"] == "arn:aws:panorama:eu-west-1:123456789012:device/test-device-name" ) assert resp["Certificates"] == b"certificate" assert resp["DeviceId"] == "device-RsozEWjZpeNe3SXHidX3mg==" assert resp["IotThingName"] == "" assert resp["Status"] == "AWAITING_PROVISIONING" @mock_aws def test_describe_device() -> None: if settings.TEST_SERVER_MODE: raise SkipTest("Can't freeze time in ServerMode") client = boto3.client("panorama", region_name="eu-west-1") given_device_name = "test-device-name" state_manager.set_transition( model_name=f"panorama::device_{given_device_name}_provisioning_status", transition={"progression": "immediate"}, ) with freeze_time("2020-01-01 12:00:00"): resp = client.provision_device( Description="test device description", Name=given_device_name, NetworkingConfiguration={ "Ethernet0": { "ConnectionType": "STATIC_IP", "StaticIpConnectionInfo": { "DefaultGateway": "192.168.1.1", "Dns": [ "8.8.8.8", ], "IpAddress": "192.168.1.10", "Mask": "255.255.255.0", }, }, "Ethernet1": { "ConnectionType": "dhcp", }, "Ntp": { "NtpServers": [ "0.pool.ntp.org", "1.pool.ntp.org", "0.fr.pool.ntp.org", ] }, }, Tags={"Key": "test-key", "Value": "test-value"}, ) resp = client.describe_device(DeviceId=resp["DeviceId"]) assert resp["AlternateSoftwares"] == [{"Version": "0.2.1"}] assert ( resp["Arn"] == "arn:aws:panorama:eu-west-1:123456789012:device/test-device-name" ) assert resp["Brand"] == "AWS_PANORAMA" assert resp["CreatedTime"] == datetime(2020, 1, 1, 12, 0, tzinfo=tzutc()) assert resp["CurrentNetworkingStatus"] == { "Ethernet0Status": { "ConnectionStatus": "CONNECTED", "HwAddress": "8C:0F:5F:60:F5:C4", "IpAddress": "192.168.1.300/24", }, "Ethernet1Status": { "ConnectionStatus": "NOT_CONNECTED", "HwAddress": "8C:0F:6F:60:F4:F1", "IpAddress": "--", }, "LastUpdatedTime": datetime(2020, 1, 1, 12, 0, tzinfo=tzutc()), "NtpStatus": { "ConnectionStatus": "CONNECTED", "IpAddress": "91.224.149.41:123", "NtpServerName": "0.pool.ntp.org", }, } assert resp["CurrentSoftware"] == "6.2.1" assert resp["Description"] == "test device description" assert resp["DeviceAggregatedStatus"] == "ONLINE" assert resp["DeviceConnectionStatus"] == "ONLINE" assert resp["DeviceId"] == "device-RsozEWjZpeNe3SXHidX3mg==" assert resp["LatestDeviceJob"] == {"JobType": "REBOOT", "Status": "COMPLETED"} assert resp["LatestSoftware"] == "6.2.1" assert resp["LeaseExpirationTime"] == datetime(2020, 1, 6, 12, 0, tzinfo=tzutc()) assert resp["Name"] == "test-device-name" assert resp["ProvisioningStatus"] == "SUCCEEDED" assert resp["SerialNumber"] == "GAD81E29013274749" assert resp["Tags"] == {"Key": "test-key", "Value": "test-value"} assert resp["Type"] == "PANORAMA_APPLIANCE" @mock_aws def test_provision_device_aggregated_status_lifecycle() -> None: if settings.TEST_SERVER_MODE: raise SkipTest("Can't use ManagedState in ServerMode") client = boto3.client("panorama", region_name="eu-west-1") given_device_name = "test-device-name" state_manager.set_transition( model_name=f"panorama::device_{given_device_name}_aggregated_status", transition={"progression": "manual", "times": 1}, ) state_manager.set_transition( model_name=f"panorama::device_{given_device_name}_provisioning_status", transition={"progression": "manual", "times": 2}, ) device_id = client.provision_device( Description="test device description", Name=given_device_name, NetworkingConfiguration={ "Ethernet0": { "ConnectionType": "STATIC_IP", "StaticIpConnectionInfo": { "DefaultGateway": "192.168.1.1", "Dns": [ "8.8.8.8", ], "IpAddress": "192.168.1.10", "Mask": "255.255.255.0", }, }, "Ethernet1": { "ConnectionType": "dhcp", }, "Ntp": { "NtpServers": [ "0.pool.ntp.org", "1.pool.ntp.org", "0.fr.pool.ntp.org", ] }, }, Tags={"Key": "test-key", "Value": "test-value"}, )["DeviceId"] resp_1 = client.describe_device(DeviceId=device_id) assert ( resp_1["Arn"] == "arn:aws:panorama:eu-west-1:123456789012:device/test-device-name" ) assert resp_1["DeviceAggregatedStatus"] == "AWAITING_PROVISIONING" assert resp_1["ProvisioningStatus"] == "AWAITING_PROVISIONING" resp_2 = client.describe_device(DeviceId=device_id) assert ( resp_2["Arn"] == "arn:aws:panorama:eu-west-1:123456789012:device/test-device-name" ) assert resp_2["DeviceAggregatedStatus"] == "PENDING" assert resp_2["ProvisioningStatus"] == "AWAITING_PROVISIONING" resp_3 = client.describe_device(DeviceId=device_id) assert ( resp_3["Arn"] == "arn:aws:panorama:eu-west-1:123456789012:device/test-device-name" ) assert resp_3["DeviceAggregatedStatus"] == "ONLINE" assert resp_3["ProvisioningStatus"] == "PENDING" @mock_aws def test_list_device() -> None: client = boto3.client("panorama", region_name="eu-west-1") resp_1 = client.provision_device( Description="test device description 1", Name="test-device-name-1", Tags={"Key": "test-key", "Value": "test-value"}, ) resp_2 = client.provision_device( Description="test device description 2", Name="test-device-name-2", Tags={"Key": "test-key", "Value": "test-value"}, ) resp = client.list_devices() assert len(resp["Devices"]) == 2 assert "Brand" in resp["Devices"][0] assert "CreatedTime" in resp["Devices"][0] assert "CurrentSoftware" in resp["Devices"][0] assert "Description" in resp["Devices"][0] assert "DeviceAggregatedStatus" in resp["Devices"][0] assert "DeviceId" in resp["Devices"][0] assert "LastUpdatedTime" in resp["Devices"][0] assert "LatestDeviceJob" in resp["Devices"][0] assert "LeaseExpirationTime" in resp["Devices"][0] assert "Name" in resp["Devices"][0] assert "ProvisioningStatus" in resp["Devices"][0] assert "Tags" in resp["Devices"][0] assert "Type" in resp["Devices"][0] assert resp["Devices"][0]["DeviceId"] == resp_1["DeviceId"] assert "Brand" in resp["Devices"][1] assert "CreatedTime" in resp["Devices"][1] assert "CurrentSoftware" in resp["Devices"][1] assert "Description" in resp["Devices"][1] assert "DeviceAggregatedStatus" in resp["Devices"][1] assert "DeviceId" in resp["Devices"][1] assert "LastUpdatedTime" in resp["Devices"][1] assert "LatestDeviceJob" in resp["Devices"][1] assert "LeaseExpirationTime" in resp["Devices"][1] assert "Name" in resp["Devices"][1] assert "ProvisioningStatus" in resp["Devices"][1] assert "Tags" in resp["Devices"][1] assert "Type" in resp["Devices"][1] assert resp["Devices"][1]["DeviceId"] == resp_2["DeviceId"] @mock_aws def test_list_device_name_filter() -> None: client = boto3.client("panorama", region_name="eu-west-1") resp_1 = client.provision_device( Description="test device description 1", Name="test-device-name-1", Tags={"Key": "test-key", "Value": "test-value"}, ) resp_2 = client.provision_device( Description="test device description 2", Name="test-device-name-2", Tags={"Key": "test-key", "Value": "test-value"}, ) _ = client.provision_device( Description="test device description 3", Name="another-test-device-name", Tags={"Key": "test-key", "Value": "test-value"}, ) resp = client.list_devices(NameFilter="test-") assert len(resp["Devices"]) == 2 assert resp["Devices"][0]["DeviceId"] == resp_1["DeviceId"] assert resp["Devices"][1]["DeviceId"] == resp_2["DeviceId"] @mock_aws def test_list_device_max_result_and_next_token() -> None: client = boto3.client("panorama", region_name="eu-west-1") _ = client.provision_device( Description="test device description 1", Name="test-device-name-1", Tags={"Key": "test-key", "Value": "test-value"}, ) _ = client.provision_device( Description="test device description 2", Name="test-device-name-2", Tags={"Key": "test-key", "Value": "test-value"}, ) resp = client.list_devices(MaxResults=1) assert len(resp["Devices"]) == 1 assert "NextToken" in resp resp = client.list_devices(MaxResults=1, NextToken=resp["NextToken"]) assert len(resp["Devices"]) == 1 assert "NextToken" not in resp @pytest.mark.parametrize( "sort_order, indexes", [ ("ASCENDING", [0, 1]), ("DESCENDING", [1, 0]), ], ) @mock_aws def test_list_devices_sort_order(sort_order: str, indexes: List[int]) -> None: client = boto3.client("panorama", region_name="eu-west-1") resp_1 = client.provision_device( Description="test device description 1", Name="test-device-name-1", Tags={"Key": "test-key", "Value": "test-value"}, ) resp_2 = client.provision_device( Description="test device description 2", Name="test-device-name-2", Tags={"Key": "test-key", "Value": "test-value"}, ) resp = client.list_devices(SortOrder=sort_order) assert len(resp["Devices"]) == 2 assert resp["Devices"][indexes[0]]["DeviceId"] == resp_1["DeviceId"] assert resp["Devices"][indexes[1]]["DeviceId"] == resp_2["DeviceId"] @pytest.mark.parametrize( "sort_by, indexes", [ ("DEVICE_ID", [0, 1]), ("CREATED_TIME", [1, 0]), ("NAME", [0, 1]), ("DEVICE_AGGREGATED_STATUS", [1, 0]), ], ) @mock_aws def test_list_devices_sort_by(sort_by: str, indexes: List[int]) -> None: if settings.TEST_SERVER_MODE: raise SkipTest("Can't freeze time in ServerMode") client = boto3.client("panorama", region_name="eu-west-1") state_manager.set_transition( model_name="panorama::device_test-device-name-2_aggregated_status", transition={"progression": "manual", "times": 1}, ) with freeze_time("2021-01-01 12:00:00"): resp_1 = client.provision_device( Description="test device description 1", Name="test-device-name-1", Tags={"Key": "test-key", "Value": "test-value"}, ) with freeze_time("2021-01-01 10:00:00"): resp_2 = client.provision_device( Description="test device description 2", Name="test-device-name-2", Tags={"Key": "test-key", "Value": "test-value"}, ) resp = client.list_devices(SortBy=sort_by) assert len(resp["Devices"]) == 2 assert resp["Devices"][indexes[0]]["DeviceId"] == resp_1["DeviceId"] assert resp["Devices"][indexes[1]]["DeviceId"] == resp_2["DeviceId"] @mock_aws def test_list_devices_device_aggregated_status_filter() -> None: if settings.TEST_SERVER_MODE: raise SkipTest("Can't use ManagedState in ServerMode") client = boto3.client("panorama", region_name="eu-west-1") state_manager.set_transition( model_name="panorama::device_test-device-name-2_aggregated_status", transition={"progression": "manual", "times": 1}, ) _ = client.provision_device( Description="test device description 1", Name="test-device-name-1", Tags={"Key": "test-key", "Value": "test-value"}, ) resp_2 = client.provision_device( Description="test device description 2", Name="test-device-name-2", Tags={"Key": "test-key", "Value": "test-value"}, ) # Need two advance to go from not-a-status to Pending client.describe_device(DeviceId=resp_2["DeviceId"]) resp = client.list_devices(DeviceAggregatedStatusFilter="PENDING") assert len(resp["Devices"]) == 1 assert resp["Devices"][0]["DeviceId"] == resp_2["DeviceId"] @mock_aws def test_update_device_metadata() -> None: client = boto3.client("panorama", region_name="eu-west-1") resp = client.provision_device( Description="test device description", Name="test-device-name" ) client.update_device_metadata( DeviceId=resp["DeviceId"], Description="updated device description", ) resp_updated = client.describe_device(DeviceId=resp["DeviceId"]) assert resp_updated["Description"] == "updated device description" @mock_aws def test_delete_device() -> None: client = boto3.client("panorama", region_name="eu-west-1") resp = client.provision_device( Description="test device description", Name="test-device-name" ) client.delete_device(DeviceId=resp["DeviceId"]) with pytest.raises(ClientError) as ex: client.describe_device(DeviceId=resp["DeviceId"]) err = ex.value.response assert err["Error"]["Code"] == "ValidationException" assert f"Device {resp['DeviceId']} not found" in err["Error"]["Message"] assert err["ResponseMetadata"]["HTTPStatusCode"] == 400