pa_api.xmlapi.types.config.address

 1# Given a list of subnets,
 2# Find all NAT rules related to an address in the subnet
 3
 4from ipaddress import IPv4Network, IPv6Network, ip_network
 5from typing import Optional, Union
 6
 7from pydantic import AliasChoices, AliasPath, Field
 8from pydantic.functional_validators import field_validator, model_validator
 9from typing_extensions import Self
10
11from pa_api.xmlapi.types.utils import List, String, XMLBaseModel
12
13IPNetwork = Union[IPv4Network, IPv6Network]
14
15
16def get_ip_network(ip_netmask):
17    try:
18        if ip_netmask:
19            return ip_network(ip_netmask, strict=False)
20    except Exception:
21        return None
22
23
24# https://docs.pydantic.dev/latest/concepts/alias/#aliaspath-and-aliaschoices
25class Address(XMLBaseModel):
26    name: str = Field(validation_alias="@name")
27    type: Optional[str] = None
28    prefix: Optional[str] = None
29    ip_netmask: Optional[str] = Field(
30        alias="ip-netmask",
31        validation_alias=AliasChoices(
32            AliasPath("ip-netmask", "#text"),
33            "ip-netmask",
34        ),
35        default=None,
36    )
37    ip_network: Optional[IPNetwork] = None
38    ip_range: Optional[str] = Field(alias="ip-range", default=None)
39    fqdn: Optional[String] = None
40    tags: List[String] = Field(
41        validation_alias=AliasPath("tag", "member"), default=None
42    )
43
44    @field_validator("tags", mode="before")
45    @classmethod
46    def validate_tags(cls, v) -> List[str]:
47        if not v:
48            return []
49        if not isinstance(v, list):
50            return [v]
51        return v
52
53    @model_validator(mode="after")
54    def validate_ip_network(self) -> Self:
55        if self.ip_network is None:
56            self.ip_network = get_ip_network(self.ip_netmask)
57        if not isinstance(self.ip_network, (IPv4Network, IPv6Network)):
58            self.ip_network = None
59        return self
60
61    @model_validator(mode="after")
62    def validate_type(self) -> Self:
63        address_type = None
64        if self.prefix:
65            address_type = "prefix"
66        elif self.ip_netmask:
67            address_type = "ip-netmask"
68        elif self.ip_range:
69            address_type = "ip-range"
70        elif self.fqdn:
71            address_type = "fqdn"
72        self.type = address_type
73        return self
74
75
76def find_addresses(tree):
77    # addresses_xml = tree.xpath(".//address/entry")
78    addresses_xml = tree.xpath("./devices/entry/device-group//address/entry")
79    address_objects = [Address.from_xml(n) for n in addresses_xml]
80
81    addresses = []
82    subnets = []
83    for a in address_objects:
84        network = a.ip_network
85        # We do not consider ip ranges for now
86        if not network:
87            continue
88        if network.prefixlen == network.max_prefixlen:
89            addresses.append(a)
90        else:
91            subnets.append(a)
92    return addresses, subnets
IPNetwork = typing.Union[ipaddress.IPv4Network, ipaddress.IPv6Network]
def get_ip_network(ip_netmask):
17def get_ip_network(ip_netmask):
18    try:
19        if ip_netmask:
20            return ip_network(ip_netmask, strict=False)
21    except Exception:
22        return None
class Address(pa_api.xmlapi.types.utils.XMLBaseModel):
26class Address(XMLBaseModel):
27    name: str = Field(validation_alias="@name")
28    type: Optional[str] = None
29    prefix: Optional[str] = None
30    ip_netmask: Optional[str] = Field(
31        alias="ip-netmask",
32        validation_alias=AliasChoices(
33            AliasPath("ip-netmask", "#text"),
34            "ip-netmask",
35        ),
36        default=None,
37    )
38    ip_network: Optional[IPNetwork] = None
39    ip_range: Optional[str] = Field(alias="ip-range", default=None)
40    fqdn: Optional[String] = None
41    tags: List[String] = Field(
42        validation_alias=AliasPath("tag", "member"), default=None
43    )
44
45    @field_validator("tags", mode="before")
46    @classmethod
47    def validate_tags(cls, v) -> List[str]:
48        if not v:
49            return []
50        if not isinstance(v, list):
51            return [v]
52        return v
53
54    @model_validator(mode="after")
55    def validate_ip_network(self) -> Self:
56        if self.ip_network is None:
57            self.ip_network = get_ip_network(self.ip_netmask)
58        if not isinstance(self.ip_network, (IPv4Network, IPv6Network)):
59            self.ip_network = None
60        return self
61
62    @model_validator(mode="after")
63    def validate_type(self) -> Self:
64        address_type = None
65        if self.prefix:
66            address_type = "prefix"
67        elif self.ip_netmask:
68            address_type = "ip-netmask"
69        elif self.ip_range:
70            address_type = "ip-range"
71        elif self.fqdn:
72            address_type = "fqdn"
73        self.type = address_type
74        return self

Usage docs: https://docs.pydantic.dev/2.9/concepts/models/

A base class for creating Pydantic models.

Attributes: __class_vars__: The names of the class variables defined on the model. __private_attributes__: Metadata about the private attributes of the model. __signature__: The synthesized __init__ [Signature][inspect.Signature] of the model.

__pydantic_complete__: Whether model building is completed, or if there are still undefined fields.
__pydantic_core_schema__: The core schema of the model.
__pydantic_custom_init__: Whether the model has a custom `__init__` function.
__pydantic_decorators__: Metadata containing the decorators defined on the model.
    This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1.
__pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to
    __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.
__pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.
__pydantic_post_init__: The name of the post-init method for the model, if defined.
__pydantic_root_model__: Whether the model is a [`RootModel`][pydantic.root_model.RootModel].
__pydantic_serializer__: The `pydantic-core` `SchemaSerializer` used to dump instances of the model.
__pydantic_validator__: The `pydantic-core` `SchemaValidator` used to validate instances of the model.

__pydantic_extra__: A dictionary containing extra values, if [`extra`][pydantic.config.ConfigDict.extra]
    is set to `'allow'`.
__pydantic_fields_set__: The names of fields explicitly set during instantiation.
__pydantic_private__: Values of private attributes set on the model instance.
name: str
type: Optional[str]
prefix: Optional[str]
ip_netmask: Optional[str]
ip_network: Union[ipaddress.IPv4Network, ipaddress.IPv6Network, NoneType]
ip_range: Optional[str]
fqdn: Optional[Annotated[str, BeforeValidator(func=<function ensure_str at 0x7f0db0c89bd0>, json_schema_input_type=PydanticUndefined)]]
tags: Annotated[List[Annotated[str, BeforeValidator(func=<function ensure_str at 0x7f0db0c89bd0>, json_schema_input_type=PydanticUndefined)]], BeforeValidator(func=<function ensure_list at 0x7f0db0c89ab0>, json_schema_input_type=PydanticUndefined)]
@field_validator('tags', mode='before')
@classmethod
def validate_tags( cls, v) -> Annotated[List[str], BeforeValidator(func=<function ensure_list at 0x7f0db0c89ab0>, json_schema_input_type=PydanticUndefined)]:
45    @field_validator("tags", mode="before")
46    @classmethod
47    def validate_tags(cls, v) -> List[str]:
48        if not v:
49            return []
50        if not isinstance(v, list):
51            return [v]
52        return v
@model_validator(mode='after')
def validate_ip_network(self) -> typing_extensions.Self:
54    @model_validator(mode="after")
55    def validate_ip_network(self) -> Self:
56        if self.ip_network is None:
57            self.ip_network = get_ip_network(self.ip_netmask)
58        if not isinstance(self.ip_network, (IPv4Network, IPv6Network)):
59            self.ip_network = None
60        return self
@model_validator(mode='after')
def validate_type(self) -> typing_extensions.Self:
62    @model_validator(mode="after")
63    def validate_type(self) -> Self:
64        address_type = None
65        if self.prefix:
66            address_type = "prefix"
67        elif self.ip_netmask:
68            address_type = "ip-netmask"
69        elif self.ip_range:
70            address_type = "ip-range"
71        elif self.fqdn:
72            address_type = "fqdn"
73        self.type = address_type
74        return self
model_config: ClassVar[pydantic.config.ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[Dict[str, pydantic.fields.FieldInfo]] = {'name': FieldInfo(annotation=str, required=True, alias_priority=2, validation_alias='@name'), 'type': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'prefix': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'ip_netmask': FieldInfo(annotation=Union[str, NoneType], required=False, default=None, alias='ip-netmask', alias_priority=2, validation_alias=AliasChoices(choices=[AliasPath(path=['ip-netmask', '#text']), 'ip-netmask'])), 'ip_network': FieldInfo(annotation=Union[IPv4Network, IPv6Network, NoneType], required=False, default=None), 'ip_range': FieldInfo(annotation=Union[str, NoneType], required=False, default=None, alias='ip-range', alias_priority=2), 'fqdn': FieldInfo(annotation=Union[Annotated[str, BeforeValidator], NoneType], required=False, default=None), 'tags': FieldInfo(annotation=List[Annotated[str, BeforeValidator]], required=False, default=None, alias_priority=2, validation_alias=AliasPath(path=['tag', 'member']), metadata=[BeforeValidator(func=<function ensure_list>, json_schema_input_type=PydanticUndefined)])}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo] objects.

This replaces Model.__fields__ from Pydantic V1.

model_computed_fields: ClassVar[Dict[str, pydantic.fields.ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

Inherited Members
pydantic.main.BaseModel
BaseModel
model_extra
model_fields_set
model_construct
model_copy
model_dump
model_dump_json
model_json_schema
model_parametrized_name
model_post_init
model_rebuild
model_validate
model_validate_json
model_validate_strings
dict
json
parse_obj
parse_raw
parse_file
from_orm
construct
copy
schema
schema_json
validate
update_forward_refs
pa_api.xmlapi.types.utils.XMLBaseModel
from_xml
def find_addresses(tree):
77def find_addresses(tree):
78    # addresses_xml = tree.xpath(".//address/entry")
79    addresses_xml = tree.xpath("./devices/entry/device-group//address/entry")
80    address_objects = [Address.from_xml(n) for n in addresses_xml]
81
82    addresses = []
83    subnets = []
84    for a in address_objects:
85        network = a.ip_network
86        # We do not consider ip ranges for now
87        if not network:
88            continue
89        if network.prefixlen == network.max_prefixlen:
90            addresses.append(a)
91        else:
92            subnets.append(a)
93    return addresses, subnets