Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
GITHUB_TOKEN: ${{ github.token }}

- name: checkout repo
uses: actions/checkout@v6
uses: actions/checkout@v7

- name: setup python 3
uses: actions/setup-python@v6
Expand All @@ -61,7 +61,7 @@ jobs:
matrix:
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
build-mode: none
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v7

- name: Initialize CodeQL
uses: github/codeql-action/init@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/dependency-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: 'Checkout repository'
uses: actions/checkout@v6
uses: actions/checkout@v7
- name: 'Dependency Review'
uses: actions/dependency-review-action@v5
with:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/e2e-test-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ jobs:

# Check out merge commit
- name: Checkout PR
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
ref: ${{ inputs.sha }}
fetch-depth: 0
Expand Down Expand Up @@ -166,7 +166,7 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
fetch-depth: 0
submodules: 'recursive'
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/e2e-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,15 @@ jobs:
steps:
- name: Clone Repository with SHA
if: ${{ inputs.sha != '' }}
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
fetch-depth: 0
submodules: 'recursive'
ref: ${{ inputs.sha }}

- name: Clone Repository without SHA
if: ${{ inputs.sha == '' }}
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
fetch-depth: 0
submodules: 'recursive'
Expand Down Expand Up @@ -127,7 +127,7 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
fetch-depth: 0
submodules: 'recursive'
Expand Down Expand Up @@ -195,7 +195,7 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
fetch-depth: 0
submodules: 'recursive'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v7
-
name: Run Labeler
uses: crazy-max/ghaction-github-labeler@548a7c3603594ec17c819e1239f281a3b801ab4d
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/nightly-smoke-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
ref: dev

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish-pypi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
environment: pypi-release
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v7

- name: Setup Python
uses: actions/setup-python@v6
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release-cross-repo-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout linode_api4 repository
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
fetch-depth: 0
submodules: 'recursive'
Expand All @@ -30,7 +30,7 @@ jobs:
python-version: '3.10'

- name: Checkout ansible repo
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
repository: linode/ansible_linode
path: .ansible/collections/ansible_collections/linode/cloud
Expand Down
94 changes: 94 additions & 0 deletions linode_api4/groups/monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ def create_alert_definition(
description: Optional[str] = None,
scope: Optional[Union[AlertScope, str]] = None,
regions: Optional[list[str]] = None,
group_by: Optional[list[str]] = None,
) -> AlertDefinition:
"""
Create a new alert definition for a given service type.
Expand Down Expand Up @@ -258,6 +259,8 @@ def create_alert_definition(
:type scope: Optional[Union[AlertScope, str]]
:param regions: (Optional) Regions to monitor.
:type regions: Optional[list[str]]
:param group_by: (Optional) Aggregates metric data by dimension so that alert conditions are evaluated independently for each dimension value.
:type group_by: Optional[list[str]]

:returns: The newly created :class:`AlertDefinition`.
:rtype: AlertDefinition
Expand All @@ -282,6 +285,8 @@ def create_alert_definition(
params["scope"] = scope
if regions is not None:
params["regions"] = regions
if group_by is not None:
params["group_by"] = group_by

# API will validate service_type and return an error if missing
result = self.client.post(
Expand All @@ -296,6 +301,95 @@ def create_alert_definition(

return AlertDefinition(self.client, result["id"], service_type, result)

def clone_alert_definition(
self,
service_type: str,
id: int,
label: str,
description: Optional[str] = None,
scope: Optional[Union[AlertScope, str]] = None,
regions: Optional[list[str]] = None,
entity_ids: Optional[list[str]] = None,
severity: Optional[int] = None,
rule_criteria: Optional[dict] = None,
trigger_conditions: Optional[dict] = None,
channel_ids: Optional[list[int]] = None,
group_by: Optional[list[str]] = None,
) -> AlertDefinition:
"""
Clone an existing alert definition for a given service type.
The clone request creates a new alert definition based on the source
definition identified by ``id``.

API Documentation: TODO

:param service_type: Service type for the source alert definition
(e.g. ``"dbaas"``).
:type service_type: str
:param id: Source alert definition identifier.
:type id: int (Alert identifier)
:param label: Human-readable label for the cloned alert definition.
This value is mandatory and must be unique.
:type label: str
:param description: (Optional) Longer description for the cloned alert definition.
:type description: Optional[str]
:param scope: (Optional) Alert scope provided in the clone request.
Scope is inherited from the source alert and is immutable.
:type scope: Optional[Union[AlertScope, str]]
:param regions: (Optional) Regions to monitor.
:type regions: Optional[list[str]]
:param entity_ids: (Optional) Restrict the alert to a subset of entity IDs.
:type entity_ids: Optional[list[str]]
:param severity: (Optional) Severity level for the alert.
:type severity: Optional[int]
:param rule_criteria: (Optional) Rule criteria used to evaluate the alert.
:type rule_criteria: Optional[dict]
:param trigger_conditions: (Optional) Trigger conditions for alert state transitions.
:type trigger_conditions: Optional[dict]
:param channel_ids: (Optional) List of alert channel IDs to notify.
:type channel_ids: Optional[list[int]]
:param group_by: (Optional) Aggregates metric data by dimension so that alert conditions are evaluated independently for each dimension value.
:type group_by: Optional[list[str]]

:returns: The newly created cloned :class:`AlertDefinition`.
:rtype: AlertDefinition
"""
params = {
"label": label,
}

if description is not None:
params["description"] = description
if scope is not None:
params["scope"] = scope
if regions is not None:
params["regions"] = regions
if entity_ids is not None:
params["entity_ids"] = entity_ids
if severity is not None:
params["severity"] = severity
if rule_criteria is not None:
params["rule_criteria"] = rule_criteria
if trigger_conditions is not None:
params["trigger_conditions"] = trigger_conditions
if channel_ids is not None:
params["channel_ids"] = channel_ids
if group_by is not None:
params["group_by"] = group_by

result = self.client.post(
f"/monitor/services/{service_type}/alert-definitions/{id}/clone",
data=params,
)

if "id" not in result:
raise UnexpectedResponseError(
"Unexpected response when cloning alert definition!",
json=result,
)

return AlertDefinition(self.client, result["id"], service_type, result)

def alert_definition_entities(
self,
service_type: str,
Expand Down
4 changes: 2 additions & 2 deletions linode_api4/groups/object_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -429,8 +429,8 @@ def bucket_create(
json=result,
)

return ObjectStorageBucket(
self.client, result["label"], result["cluster"], result
return ObjectStorageBucket.make_instance(
result["label"], self.client, json=result
)

def object_acl_config(self, cluster_or_region_id: str, bucket, name=None):
Expand Down
25 changes: 24 additions & 1 deletion linode_api4/groups/vpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@

from linode_api4.errors import UnexpectedResponseError
from linode_api4.groups import Group
from linode_api4.objects import VPC, Region, VPCIPAddress, VPCIPv6RangeOptions
from linode_api4.objects import (
VPC,
Region,
VPCIPAddress,
VPCIPv4DefaultRange,
VPCIPv4RangeOptions,
VPCIPv6RangeOptions,
)
from linode_api4.objects.base import _flatten_request_body_recursive
from linode_api4.paginated_list import PaginatedList
from linode_api4.util import drop_null_keys
Expand Down Expand Up @@ -36,6 +43,7 @@ def create(
description: Optional[str] = None,
subnets: Optional[List[Dict[str, Any]]] = None,
ipv6: Optional[List[Union[VPCIPv6RangeOptions, Dict[str, Any]]]] = None,
ipv4: Optional[List[Union[VPCIPv4RangeOptions, Dict[str, Any]]]] = None,
**kwargs,
) -> VPC:
"""
Expand All @@ -53,6 +61,8 @@ def create(
:type subnets: List[Dict[str, Any]]
:param ipv6: The IPv6 address ranges for this VPC.
:type ipv6: List[Union[VPCIPv6RangeOptions, Dict[str, Any]]]
:param ipv4: The IPv4 address ranges for this VPC. Note that IPv4 VPCs may not currently be available to all users.
:type ipv4: List[Union[VPCIPv4RangeOptions, Dict[str, Any]]]

:returns: The new VPC object.
:rtype: VPC
Expand All @@ -61,6 +71,7 @@ def create(
"label": label,
"region": region.id if isinstance(region, Region) else region,
"description": description,
"ipv4": ipv4,
"ipv6": ipv6,
"subnets": subnets,
}
Expand Down Expand Up @@ -108,3 +119,15 @@ def ips(self, *filters) -> PaginatedList:
return self.client._get_and_filter(
VPCIPAddress, *filters, endpoint="/vpcs/ips"
)

def default_ranges(self) -> VPCIPv4DefaultRange:
"""
Retrieve the default settings for the internal and forbidden IPv4 address ranges in VPCs.

API Documentation: https://techdocs.akamai.com/linode-api/reference/get-vpcs-default-ranges

:returns: The default IPv4 ranges for VPCs.
:rtype: VPCIPv4DefaultRange
"""
result = self.client.get("/vpcs/default-ranges")
return VPCIPv4DefaultRange.from_json(result)
1 change: 1 addition & 0 deletions linode_api4/objects/monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,7 @@ class AlertDefinition(DerivedBase):
"regions": Property(mutable=True),
"entities": Property(json_object=AlertEntities),
"channel_ids": Property(mutable=True),
"group_by": Property(mutable=True),
}


Expand Down
34 changes: 34 additions & 0 deletions linode_api4/objects/vpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,36 @@
from linode_api4.util import drop_null_keys


@dataclass
class VPCIPv4DefaultRange(JSONObject):
"""
VPCIPv4DefaultRange represents the default settings for the internal and forbidden IPv4 address ranges in VPCs.
"""

default_ipv4_ranges: Optional[List[str]] = None
forbidden_ipv4_ranges: Optional[List[str]] = None


@dataclass
class VPCIPv4RangeOptions(JSONObject):
"""
VPCIPv4RangeOptions is used to specify an IPv4 range when creating or updating a VPC.
"""

range: Optional[str] = None


@dataclass
class VPCIPv4Range(JSONObject):
"""
VPCIPv4Range represents a single VPC IPv4 range.
"""

put_class = VPCIPv4RangeOptions

range: str = ""


@dataclass
class VPCIPv6RangeOptions(JSONObject):
"""
Expand Down Expand Up @@ -108,6 +138,10 @@ class VPC(Base):
"label": Property(mutable=True),
"description": Property(mutable=True),
"region": Property(slug_relationship=Region),
# Note that IPv4 VPCs may not currently be available to all users.
"ipv4": Property(
json_object=VPCIPv4Range, mutable=True, unordered=True
),
"ipv6": Property(json_object=VPCIPv6Range, unordered=True),
"subnets": Property(derived_class=VPCSubnet),
"created": Property(is_datetime=True),
Expand Down
3 changes: 3 additions & 0 deletions test/fixtures/monitor_alert-definitions.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
"description": "A test alert for dbaas service",
"scope": "entity",
"regions": [],
"group_by": [
"entity_id"
],
"entities": {
"url": "/monitor/services/dbaas/alert-definitions/12345/entities",
"count": 1,
Expand Down
Loading
Loading