From d5e0aa93e7b6ff920d3bac539fc1394b8fbe5965 Mon Sep 17 00:00:00 2001 From: Ye Chen Date: Wed, 18 Feb 2026 15:09:07 -0500 Subject: [PATCH 1/2] init --- linode_api4/groups/linode.py | 8 ++++ linode_api4/objects/linode.py | 34 +++++++++++++- test/fixtures/linode_instances.json | 12 +++-- test/fixtures/linode_instances_123_clone.json | 45 +++++++++++++++++++ .../models/sharegroups/test_sharegroups.py | 4 +- test/unit/objects/linode_test.py | 29 ++++++++++++ 6 files changed, 125 insertions(+), 7 deletions(-) create mode 100644 test/fixtures/linode_instances_123_clone.json diff --git a/linode_api4/groups/linode.py b/linode_api4/groups/linode.py index e32a284f1..2b3490281 100644 --- a/linode_api4/groups/linode.py +++ b/linode_api4/groups/linode.py @@ -18,6 +18,7 @@ from linode_api4.objects.filtering import Filter from linode_api4.objects.linode import ( Backup, + InstanceACLPAlertsOptions, InstancePlacementGroupAssignment, InterfaceGeneration, NetworkInterface, @@ -140,6 +141,9 @@ def instance_create( region, image=None, authorized_keys=None, + alerts: Optional[ + Union[Dict[str, Any], InstanceACLPAlertsOptions] + ] = None, firewall: Optional[Union[Firewall, int]] = None, backup: Optional[Union[Backup, int]] = None, stackscript: Optional[Union[StackScript, int]] = None, @@ -319,6 +323,9 @@ def instance_create( The contents of this field can be built using the :any:`build_instance_metadata` method. :type metadata: dict + :param alerts: ACLP alerts available to define during instance creation. + This is v4beta only and may not be available to all users. + :type alerts: dict[str, Any] or InstanceACLPAlertsOptions :param firewall: The firewall to attach this Linode to. :type firewall: int or Firewall :param disk_encryption: The disk encryption policy for this Linode. @@ -356,6 +363,7 @@ def instance_create( "region": region, "image": image, "authorized_keys": load_and_validate_keys(authorized_keys), + "alerts": alerts, # These will automatically be flattened below "firewall_id": firewall, "backup_id": backup, diff --git a/linode_api4/objects/linode.py b/linode_api4/objects/linode.py index 1edf4e014..393842fee 100644 --- a/linode_api4/objects/linode.py +++ b/linode_api4/objects/linode.py @@ -766,6 +766,31 @@ class UpgradeInterfacesResult(JSONObject): ) +@dataclass +class InstanceAlerts(JSONObject): + """ + Represents both legacy and ACLP alerts for a Linode Instance. + """ + + cpu: int = 0 + io: int = 0 + network_in: int = 0 + network_out: int = 0 + transfer_quota: int = 0 + system_alerts: List[int] = field(default_factory=list) + user_alerts: List[int] = field(default_factory=list) + + +@dataclass +class InstanceACLPAlertsOptions(JSONObject): + """ + Represents the ACLP alerts available to define during instance creation and cloning. + """ + + system_alerts: List[int] = field(default_factory=list) + user_alerts: List[int] = field(default_factory=list) + + class Instance(Base): """ A Linode Instance. @@ -782,7 +807,7 @@ class Instance(Base): "created": Property(is_datetime=True), "updated": Property(volatile=True, is_datetime=True), "region": Property(slug_relationship=Region), - "alerts": Property(mutable=True), + "alerts": Property(mutable=True, json_object=InstanceAlerts), "image": Property(slug_relationship=Image), "disks": Property(derived_class=Disk), "configs": Property(derived_class=Config), @@ -1822,6 +1847,9 @@ def clone( label=None, group=None, with_backups=None, + alerts: Optional[ + Union[Dict[str, Any], InstanceACLPAlertsOptions] + ] = None, placement_group: Union[ InstancePlacementGroupAssignment, "PlacementGroup", @@ -1863,6 +1891,9 @@ def clone( enrolled in the Linode Backup service. This will incur an additional charge. :type: with_backups: bool + :param alerts: ACLP monitor alert definitions associate with the cloned Instance. + This is under v4beta and may not be available to all users. + :type alerts: dict[str, Any] or InstanceACLPAlertsOptions :param placement_group: Information about the placement group to create this instance under. :type placement_group: Union[InstancePlacementGroupAssignment, PlacementGroup, Dict[str, Any], int] @@ -1893,6 +1924,7 @@ def clone( "label": label, "group": group, "with_backups": with_backups, + "alerts": alerts, "placement_group": _expand_placement_group_assignment( placement_group ), diff --git a/test/fixtures/linode_instances.json b/test/fixtures/linode_instances.json index 08cbe80c8..e52eaa73d 100644 --- a/test/fixtures/linode_instances.json +++ b/test/fixtures/linode_instances.json @@ -14,7 +14,9 @@ "network_out": 5, "cpu": 90, "transfer_quota": 80, - "io": 5000 + "io": 5000, + "system_alerts": [123,456], + "user_alerts": [555] }, "label": "linode123", "backups": { @@ -64,7 +66,9 @@ "network_out": 5, "cpu": 90, "transfer_quota": 80, - "io": 5000 + "io": 5000, + "system_alerts": [123,456], + "user_alerts": [555] }, "label": "linode456", "backups": { @@ -104,7 +108,9 @@ "network_out": 5, "cpu": 90, "transfer_quota": 80, - "io": 5000 + "io": 5000, + "system_alerts": [123,456], + "user_alerts": [555] }, "group": "test", "hypervisor": "kvm", diff --git a/test/fixtures/linode_instances_123_clone.json b/test/fixtures/linode_instances_123_clone.json new file mode 100644 index 000000000..bcfe6b695 --- /dev/null +++ b/test/fixtures/linode_instances_123_clone.json @@ -0,0 +1,45 @@ +{ + "id": 124, + "status": "running", + "type": "g6-standard-1", + "alerts": { + "network_in": 5, + "network_out": 5, + "cpu": 90, + "transfer_quota": 80, + "io": 5000, + "system_alerts": [123,456], + "user_alerts": [555] + }, + "group": "test", + "hypervisor": "kvm", + "label": "linode124", + "backups": { + "enabled": true, + "schedule": { + "window": "W02", + "day": "Scheduling" + } + }, + "specs": { + "memory": 2048, + "disk": 30720, + "vcpus": 1, + "transfer": 2000 + }, + "ipv6": "1235:abcd::1234:abcd:89ef:67cd/64", + "created": "2017-01-01T00:00:00", + "region": "us-east-1", + "ipv4": [ + "124.45.67.89" + ], + "updated": "2017-01-01T00:00:00", + "image": "linode/ubuntu24.04", + "tags": ["something"], + "host_uuid": "3b3ddd59d9a78bb8de041391075df44de62bfec8", + "watchdog_enabled": true, + "disk_encryption": "disabled", + "lke_cluster_id": null, + "placement_group": null, + "interface_generation": "linode" +} \ No newline at end of file diff --git a/test/integration/models/sharegroups/test_sharegroups.py b/test/integration/models/sharegroups/test_sharegroups.py index 9c66bad90..57aced4bb 100644 --- a/test/integration/models/sharegroups/test_sharegroups.py +++ b/test/integration/models/sharegroups/test_sharegroups.py @@ -1,8 +1,6 @@ import datetime from test.integration.conftest import get_region -from test.integration.helpers import ( - get_test_label, -) +from test.integration.helpers import get_test_label import pytest diff --git a/test/unit/objects/linode_test.py b/test/unit/objects/linode_test.py index 40bbb5069..d2037c392 100644 --- a/test/unit/objects/linode_test.py +++ b/test/unit/objects/linode_test.py @@ -25,6 +25,7 @@ Disk, Image, Instance, + InstanceACLPAlertsOptions, StackScript, Type, VPCSubnet, @@ -58,6 +59,14 @@ def test_get_linode(self): self.assertEqual(linode.lke_cluster_id, None) self.assertEqual(linode.maintenance_policy, "linode/migrate") + self.assertEqual(linode.alerts.cpu, 90) + self.assertEqual(linode.alerts.io, 5000) + self.assertEqual(linode.alerts.network_in, 5) + self.assertEqual(linode.alerts.network_out, 5) + self.assertEqual(linode.alerts.transfer_quota, 80) + self.assertEqual(linode.alerts.system_alerts, [123, 456]) + self.assertEqual(linode.alerts.user_alerts, [555]) + json = linode._raw_json self.assertIsNotNone(json) self.assertEqual(json["id"], 123) @@ -183,6 +192,8 @@ def test_update_linode(self): "network_in": 5, "network_out": 5, "transfer_quota": 80, + "system_alerts": [123, 456], + "user_alerts": [555], }, "backups": { "enabled": True, @@ -641,6 +652,24 @@ def test_create_interface_vlan(self): LinodeInterfaceTest.assert_linode_124_interface_789(result) + def test_instance_clone_with_alerts(self): + src = Instance(self.client, 123) + + with self.mock_post("linode/instances/123/clone") as m: + src.clone( + region="us-east", + instance_type="g6-standard-1", + alerts=InstanceACLPAlertsOptions( + system_alerts=[123, 456], + user_alerts=[555], + ), + ) + + assert m.call_data["alerts"] == { + "system_alerts": [123, 456], + "user_alerts": [555], + } + class DiskTest(ClientBaseCase): """ From 7c549b656e4c43a7870405d24da1f940ec7af422 Mon Sep 17 00:00:00 2001 From: Ye Chen Date: Wed, 18 Feb 2026 16:43:16 -0500 Subject: [PATCH 2/2] address comments --- linode_api4/groups/linode.py | 12 ++++++------ linode_api4/objects/linode.py | 21 +++++++++++---------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/linode_api4/groups/linode.py b/linode_api4/groups/linode.py index 2b3490281..583a00cb5 100644 --- a/linode_api4/groups/linode.py +++ b/linode_api4/groups/linode.py @@ -141,9 +141,6 @@ def instance_create( region, image=None, authorized_keys=None, - alerts: Optional[ - Union[Dict[str, Any], InstanceACLPAlertsOptions] - ] = None, firewall: Optional[Union[Firewall, int]] = None, backup: Optional[Union[Backup, int]] = None, stackscript: Optional[Union[StackScript, int]] = None, @@ -166,6 +163,9 @@ def instance_create( interface_generation: Optional[Union[InterfaceGeneration, str]] = None, network_helper: Optional[bool] = None, maintenance_policy: Optional[str] = None, + alerts: Optional[ + Union[Dict[str, Any], InstanceACLPAlertsOptions] + ] = None, **kwargs, ): """ @@ -323,9 +323,6 @@ def instance_create( The contents of this field can be built using the :any:`build_instance_metadata` method. :type metadata: dict - :param alerts: ACLP alerts available to define during instance creation. - This is v4beta only and may not be available to all users. - :type alerts: dict[str, Any] or InstanceACLPAlertsOptions :param firewall: The firewall to attach this Linode to. :type firewall: int or Firewall :param disk_encryption: The disk encryption policy for this Linode. @@ -343,6 +340,9 @@ def instance_create( :param maintenance_policy: The slug of the maintenance policy to apply during maintenance. If not provided, the default policy (linode/migrate) will be applied. :type maintenance_policy: str + :param alerts: ACLP alerts available to define during instance creation. + This is v4beta only and may not be available to all users. + :type alerts: dict[str, Any] or InstanceACLPAlertsOptions :returns: A new Instance object, or a tuple containing the new Instance and the generated password. diff --git a/linode_api4/objects/linode.py b/linode_api4/objects/linode.py index 393842fee..585c4b9b8 100644 --- a/linode_api4/objects/linode.py +++ b/linode_api4/objects/linode.py @@ -777,8 +777,8 @@ class InstanceAlerts(JSONObject): network_in: int = 0 network_out: int = 0 transfer_quota: int = 0 - system_alerts: List[int] = field(default_factory=list) - user_alerts: List[int] = field(default_factory=list) + system_alerts: Optional[List[int]] = None + user_alerts: Optional[List[int]] = None @dataclass @@ -787,8 +787,8 @@ class InstanceACLPAlertsOptions(JSONObject): Represents the ACLP alerts available to define during instance creation and cloning. """ - system_alerts: List[int] = field(default_factory=list) - user_alerts: List[int] = field(default_factory=list) + system_alerts: Optional[List[int]] = None + user_alerts: Optional[List[int]] = None class Instance(Base): @@ -1847,15 +1847,15 @@ def clone( label=None, group=None, with_backups=None, - alerts: Optional[ - Union[Dict[str, Any], InstanceACLPAlertsOptions] - ] = None, placement_group: Union[ InstancePlacementGroupAssignment, "PlacementGroup", Dict[str, Any], int, ] = None, + alerts: Optional[ + Union[Dict[str, Any], InstanceACLPAlertsOptions] + ] = None, ): """ Clones this linode into a new linode or into a new linode in the given region @@ -1891,12 +1891,13 @@ def clone( enrolled in the Linode Backup service. This will incur an additional charge. :type: with_backups: bool - :param alerts: ACLP monitor alert definitions associate with the cloned Instance. - This is under v4beta and may not be available to all users. - :type alerts: dict[str, Any] or InstanceACLPAlertsOptions :param placement_group: Information about the placement group to create this instance under. :type placement_group: Union[InstancePlacementGroupAssignment, PlacementGroup, Dict[str, Any], int] + :param alerts: ACLP monitor alert definitions associated with the cloned Instance. + This is under v4beta and may not be available to all users. + :type alerts: dict[str, Any] or InstanceACLPAlertsOptions + :returns: The cloned Instance. :rtype: Instance """