Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Commit

Permalink
[Feature Templates] Generator update (#371)
Browse files Browse the repository at this point in the history
* renaming template fields + unittests

* updating generator to include data paths

* lowercase protocol values

* refactor + unittests

* unittests for feature template models

---------

Co-authored-by: Mateusz Lapinski <[email protected]>
  • Loading branch information
lapson97 and Mateusz Lapinski authored Sep 21, 2023
1 parent 1183c5c commit 722cbbd
Show file tree
Hide file tree
Showing 40 changed files with 5,159 additions and 689 deletions.
29 changes: 6 additions & 23 deletions vmngclient/api/template_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -559,32 +559,15 @@ def generate_feature_template_payload(
if field.key in template.device_specific_variables:
value = template.device_specific_variables[field.key]
else:
# Iterate through every possible field, maybe refactor(?)
# Use data_path instead. data_path as tuple
# next(field_value.field_info.extra.get("vmanage_key") == field.key, template.__fields__.values())
for field_name, field_value in template.__fields__.items():
if "vmanage_key" in field_value.field_info.extra: # type: ignore
vmanage_key = field_value.field_info.extra.get("vmanage_key") # type: ignore
if vmanage_key != field.key:
break

value = template.dict(by_alias=True).get(field_name, None)
field_value.field_info.extra.pop("vmanage_key") # type: ignore
if (
field.dataPath == field_value.field_info.extra.get("data_path", []) # type: ignore
and field.key == field_value.alias
):
value = getattr(template, field_name)
break
if value is None:
value = template.dict(by_alias=True).get(field.key, None)

# TODO remove workaround, add specific object
# types like Ignore, Constant, None etc so generator will now
# which object to ommit while generating payload
if template.type == "cisco_vpn_interface" and value is None:
continue

if template.type == "cisco_ospf" and value is None:
continue

if isinstance(value, bool):
value = str(value).lower() # type: ignore
continue

# Merge dictionaries

Expand Down
37 changes: 24 additions & 13 deletions vmngclient/api/templates/feature_template_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
from typing import Any, Dict, List, Optional

from pydantic import BaseModel, Field, validator
from pydantic.fields import ModelField # type: ignore

from vmngclient.api.templates.device_variable import DeviceVariable
from vmngclient.api.templates.feature_template import FeatureTemplate


class FeatureTemplateOptionType(str, Enum):
Expand Down Expand Up @@ -91,15 +93,22 @@ def payload_scheme(self, value: Any = None, help=None, current_path=None) -> dic

output["vipObjectType"] = self.objectType.value

def nest_value_in_output(value: Any) -> dict:
pointer = rel_output
for path in self.dataPath:
pointer = pointer[path]
pointer[self.key] = value
return rel_output

if isinstance(value, DeviceVariable):
vip_variable = VipVariable(
vipValue="",
vipType=FeatureTemplateOptionType.VARIABLE_NAME,
vipObjectType=self.objectType,
vipVariableName=value.name,
)
return nest_value_in_output(vip_variable.dict(by_alias=True, exclude_none=True))

return {self.key: vip_variable.dict(by_alias=True, exclude_none=True)}
else:
if value:
output["vipType"] = FeatureTemplateOptionType.CONSTANT.value
Expand All @@ -111,19 +120,27 @@ def payload_scheme(self, value: Any = None, help=None, current_path=None) -> dic
for child in self.children: # Child in schema
if current_path is None:
current_path = []
child_payload.update(
child.payload_scheme(
obj[child.key], help=output, current_path=self.dataPath + [self.key]
obj: FeatureTemplate # type: ignore
model_field: ModelField = next(
filter(
lambda f: f.field_info.extra.get("data_path", []) == child.dataPath
and f.alias == child.key,
obj.__fields__.values(),
)
)
obj_value = getattr(obj, model_field.name)
child_payload.update(
child.payload_scheme(obj_value, help=output, current_path=self.dataPath + [self.key])
)
children_output.append(child_payload)
output["vipValue"] = children_output
else:
output["vipValue"] = value
else:
if "default" in self.dataType:
output["vipValue"] = self.dataType["default"] if value is None else value
output["vipType"] = self.defaultOption.value
return {}
# output["vipValue"] = self.dataType["default"] if value is None else value
# output["vipType"] = self.defaultOption.value
else:
output["vipValue"] = []
output["vipType"] = FeatureTemplateOptionType.IGNORE.value
Expand All @@ -135,10 +152,4 @@ def payload_scheme(self, value: Any = None, help=None, current_path=None) -> dic
if self.primaryKeys:
output["vipPrimaryKey"] = self.primaryKeys

pointer = rel_output

for path in self.dataPath:
pointer = pointer[path]

pointer[self.key] = output
return rel_output
return nest_value_in_output(output)
4 changes: 2 additions & 2 deletions vmngclient/api/templates/models/cisco_banner_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ class Config:
arbitrary_types_allowed = True
allow_population_by_field_name = True

login_banner: Optional[str] = Field(vmanage_key="login")
motd_banner: Optional[str] = Field(vmanage_key="motd")
login_banner: Optional[str] = Field(alias="login")
motd_banner: Optional[str] = Field(alias="motd")

payload_path: ClassVar[Path] = Path(__file__).parent / "DEPRECATED"
type: ClassVar[str] = "cisco_banner"
4 changes: 2 additions & 2 deletions vmngclient/api/templates/models/cisco_bfd_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ class Config:
arbitrary_types_allowed = True
allow_population_by_field_name = True

multiplier: Optional[int] = DEFAULT_BFD_MULTIPLIER
poll_interval: Optional[int] = Field(DEFAULT_BFD_POLL_INTERVAL, alias="poll-interval")
multiplier: Optional[int] = Field(DEFAULT_BFD_MULTIPLIER, data_path=["app-route"])
poll_interval: Optional[int] = Field(DEFAULT_BFD_POLL_INTERVAL, alias="poll-interval", data_path=["app-route"])
default_dscp: Optional[int] = Field(DEFAULT_BFD_DSCP, alias="default-dscp")
color: Optional[List[Color]]

Expand Down
84 changes: 42 additions & 42 deletions vmngclient/api/templates/models/cisco_bgp_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,10 @@ class AddressFamily(BaseModel):
ipv6_aggregate_address: Optional[List[Ipv6AggregateAddress]] = Field(alias="ipv6-aggregate-address")
network: Optional[List[Network]]
ipv6_network: Optional[List[Ipv6Network]] = Field(alias="ipv6-network")
paths: Optional[int]
originate: Optional[bool]
name: Optional[str]
filter: Optional[bool]
paths: Optional[int] = Field(data_path=["maximum-paths"])
originate: Optional[bool] = Field(data_path=["default-information"])
policy_name: Optional[str] = Field(data_path=["table-map"], alias="name")
filter: Optional[bool] = Field(data_path=["table-map"])
redistribute: Optional[List[Redistribute]]

class Config:
Expand Down Expand Up @@ -141,10 +141,10 @@ class Config:

class NeighborAddressFamily(BaseModel):
family_type: NeighborFamilyType = Field(alias="family-type")
prefix_num: Optional[int] = Field(alias="prefix-num")
threshold: Optional[int]
restart: Optional[int]
warning_only: Optional[bool] = Field(alias="warning-only")
prefix_num: Optional[int] = Field(data_path=["maximum-prefixes"], alias="prefix-num")
threshold: Optional[int] = Field(data_path=["maximum-prefixes"])
restart: Optional[int] = Field(data_path=["maximum-prefixes"])
warning_only: Optional[bool] = Field(data_path=["maximum-prefixes"], alias="warning-only")
route_policy: Optional[List[RoutePolicy]] = Field(alias="route-policy")

class Config:
Expand All @@ -156,9 +156,9 @@ class Neighbor(BaseModel):
description: Optional[str]
shutdown: Optional[bool]
remote_as: int = Field(alias="remote-as")
keepalive: Optional[int]
holdtime: Optional[int]
if_name: Optional[str] = Field(alias="if-name")
keepalive: Optional[int] = Field(data_path=["timers"])
holdtime: Optional[int] = Field(data_path=["timers"])
if_name: Optional[str] = Field(data_path=["update-source"], alias="if-name")
next_hop_self: Optional[bool] = Field(alias="next-hop-self")
send_community: Optional[bool] = Field(alias="send-community")
send_ext_community: Optional[bool] = Field(alias="send-ext-community")
Expand All @@ -167,7 +167,7 @@ class Neighbor(BaseModel):
send_label: Optional[bool] = Field(alias="send-label")
send_label_explicit: Optional[bool] = Field(alias="send-label-explicit")
as_override: Optional[bool] = Field(alias="as-override")
as_number: Optional[int] = Field(alias="as-number")
as_number: Optional[int] = Field(data_path=["allowas-in"], alias="as-number")
address_family: Optional[List[NeighborAddressFamily]] = Field(alias="address-family")

class Config:
Expand All @@ -193,10 +193,10 @@ class IPv6NeighborFamilyType(str, Enum):

class IPv6NeighborAddressFamily(BaseModel):
family_type: IPv6NeighborFamilyType = Field(alias="family-type")
prefix_num: Optional[int] = Field(0, alias="prefix-num")
threshold: Optional[int]
restart: Optional[int]
warning_only: Optional[bool] = Field(False, alias="warning-only")
prefix_num: Optional[int] = Field(0, data_path=["maximum-prefixes"], alias="prefix-num")
threshold: Optional[int] = Field(data_path=["maximum-prefixes"])
restart: Optional[int] = Field(data_path=["maximum-prefixes"])
warning_only: Optional[bool] = Field(False, data_path=["maximum-prefixes"], alias="warning-only")
route_policy: Optional[List[RoutePolicy]] = Field(alias="route-policy")

class Config:
Expand All @@ -208,9 +208,9 @@ class Ipv6Neighbor(BaseModel):
description: Optional[str]
shutdown: Optional[bool]
remote_as: int = Field(alias="remote-as")
keepalive: Optional[int]
holdtime: Optional[int]
if_name: Optional[str] = Field(alias="if-name")
keepalive: Optional[int] = Field(data_path=["timers"])
holdtime: Optional[int] = Field(data_path=["timers"])
if_name: Optional[str] = Field(data_path=["update-source"], alias="if-name")
next_hop_self: Optional[bool] = Field(False, alias="next-hop-self")
send_community: Optional[bool] = Field(True, alias="send-community")
send_ext_community: Optional[bool] = Field(True, alias="send-ext-community")
Expand All @@ -219,7 +219,7 @@ class Ipv6Neighbor(BaseModel):
send_label: Optional[bool] = Field(False, alias="send-label")
send_label_explicit: Optional[bool] = Field(False, alias="send-label-explicit")
as_override: Optional[bool] = Field(False, alias="as-override")
as_number: Optional[int] = Field(alias="as-number")
as_number: Optional[int] = Field(data_path=["allowas-in"], alias="as-number")
address_family: Optional[List[IPv6NeighborAddressFamily]] = Field(alias="address-family")

class Config:
Expand All @@ -244,32 +244,32 @@ class Config:
arbitrary_types_allowed = True
allow_population_by_field_name = True

as_num: Optional[str] = Field(alias="as-num")
shutdown: Optional[bool]
router_id: Optional[str] = Field(alias="router-id")
propagate_aspath: Optional[bool] = Field(alias="propagate-aspath")
propagate_community: Optional[bool] = Field(alias="propagate-community")
route_target_ipv4: List[RouteTargetIpv4] = Field([], alias="route-target-ipv4")
route_target_ipv6: List[RouteTargetIpv6] = Field([], alias="route-target-ipv6")
mpls_interface: Optional[List[MplsInterface]] = Field(alias="mpls-interface")
external: Optional[int]
internal: Optional[int]
local: Optional[int]
keepalive: Optional[int]
holdtime: Optional[int]
always_compare: Optional[bool] = Field(alias="always-compare")
deterministic: Optional[bool]
missing_as_worst: Optional[bool] = Field(alias="missing-as-worst")
compare_router_id: Optional[bool] = Field(alias="compare-router-id")
multipath_relax: Optional[bool] = Field(alias="multipath-relax")
address_family: Optional[List[AddressFamily]] = Field(alias="address-family")
neighbor: Optional[List[Neighbor]]
ipv6_neighbor: Optional[List[Ipv6Neighbor]] = Field(alias="ipv6-neighbor")
as_num: Optional[str] = Field(data_path=["bgp"], alias="as-num")
shutdown: Optional[bool] = Field(data_path=["bgp"])
router_id: Optional[str] = Field(data_path=["bgp"], alias="router-id")
propagate_aspath: Optional[bool] = Field(data_path=["bgp"], alias="propagate-aspath")
propagate_community: Optional[bool] = Field(data_path=["bgp"], alias="propagate-community")
route_target_ipv4: List[RouteTargetIpv4] = Field([], data_path=["bgp", "target"], alias="route-target-ipv4")
route_target_ipv6: List[RouteTargetIpv6] = Field([], data_path=["bgp", "target"], alias="route-target-ipv6")
mpls_interface: Optional[List[MplsInterface]] = Field(data_path=["bgp"], alias="mpls-interface")
external: Optional[int] = Field(data_path=["bgp", "distance"])
internal: Optional[int] = Field(data_path=["bgp", "distance"])
local: Optional[int] = Field(data_path=["bgp", "distance"])
keepalive: Optional[int] = Field(data_path=["bgp", "timers"])
holdtime: Optional[int] = Field(data_path=["bgp", "timers"])
always_compare: Optional[bool] = Field(data_path=["bgp", "best-path", "med"], alias="always-compare")
deterministic: Optional[bool] = Field(data_path=["bgp", "best-path", "med"])
missing_as_worst: Optional[bool] = Field(data_path=["bgp", "best-path", "med"], alias="missing-as-worst")
compare_router_id: Optional[bool] = Field(data_path=["bgp", "best-path"], alias="compare-router-id")
multipath_relax: Optional[bool] = Field(data_path=["bgp", "best-path", "as-path"], alias="multipath-relax")
address_family: Optional[List[AddressFamily]] = Field(data_path=["bgp"], alias="address-family")
neighbor: Optional[List[Neighbor]] = Field(data_path=["bgp"])
ipv6_neighbor: Optional[List[Ipv6Neighbor]] = Field(data_path=["bgp"], alias="ipv6-neighbor")

payload_path: ClassVar[Path] = Path(__file__).parent / "DEPRECATED"
type: ClassVar[str] = "cisco_bgp"

@validator("shutdown")
@validator("shutdown", "deterministic", "missing_as_worst", "compare_router_id", "multipath_relax")
def cast_to_str(cls, value):
if value is not None:
return str(value).lower()
22 changes: 11 additions & 11 deletions vmngclient/api/templates/models/cisco_logging_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ class AuthType(str, Enum):

class TlsProfile(ConvertBoolToStringModel):
profile: str
version: Optional[Version] = Version.TLSV11
version: Optional[Version] = Field(Version.TLSV11, data_path=["tls-version"])
auth_type: AuthType = Field(alias="auth-type")
ciphersuite_list: Optional[List] = Field(alias="ciphersuite-list")
ciphersuite_list: Optional[List] = Field(data_path=["ciphersuite"], alias="ciphersuite-list")

class Config:
allow_population_by_field_name = True
Expand All @@ -47,9 +47,9 @@ class Server(ConvertBoolToStringModel):
vpn: Optional[int]
source_interface: Optional[str] = Field(alias="source-interface")
priority: Optional[Priority] = Priority.INFORMATION
enable_tls: Optional[bool] = Field(False, alias="enable-tls")
custom_profile: Optional[bool] = Field(False, alias="custom-profile")
profile: Optional[str]
enable_tls: Optional[bool] = Field(False, data_path=["tls"], alias="enable-tls")
custom_profile: Optional[bool] = Field(False, data_path=["tls", "tls-properties"], alias="custom-profile")
profile: Optional[str] = Field(data_path=["tls", "tls-properties"])

class Config:
allow_population_by_field_name = True
Expand All @@ -60,9 +60,9 @@ class Ipv6Server(ConvertBoolToStringModel):
vpn: Optional[int]
source_interface: Optional[str] = Field(alias="source-interface")
priority: Optional[Priority] = Priority.INFORMATION
enable_tls: Optional[bool] = Field(False, alias="enable-tls")
custom_profile: Optional[bool] = Field(False, alias="custom-profile")
profile: Optional[str]
enable_tls: Optional[bool] = Field(False, data_path=["tls"], alias="enable-tls")
custom_profile: Optional[bool] = Field(False, data_path=["tls", "tls-properties"], alias="custom-profile")
profile: Optional[str] = Field(data_path=["tls", "tls-properties"])

class Config:
allow_population_by_field_name = True
Expand All @@ -73,9 +73,9 @@ class Config:
arbitrary_types_allowed = True
allow_population_by_field_name = True

enable: Optional[bool] = True
size: Optional[int] = DEFAULT_LOGGING_SIZE
rotate: Optional[int] = DEFAULT_LOGGING_ROTATE
enable: Optional[bool] = Field(True, data_path=["disk"])
size: Optional[int] = Field(DEFAULT_LOGGING_SIZE, data_path=["disk", "file"])
rotate: Optional[int] = Field(DEFAULT_LOGGING_ROTATE, data_path=["disk", "file"])
tls_profile: List[TlsProfile] = Field(alias="tls-profile")
server: Optional[List[Server]]
ipv6_server: Optional[List[Ipv6Server]] = Field(alias="ipv6-server")
Expand Down
15 changes: 8 additions & 7 deletions vmngclient/api/templates/models/cisco_ntp_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
from pydantic import BaseModel, Field

from vmngclient.api.templates.feature_template import FeatureTemplate
from vmngclient.utils.pydantic_validators import ConvertBoolToStringModel


class Server(BaseModel):
class Server(ConvertBoolToStringModel):
class Config:
allow_population_by_field_name = True

Expand All @@ -26,17 +27,17 @@ class Config:
md5: str


class CiscoNTPModel(FeatureTemplate):
class CiscoNTPModel(FeatureTemplate, ConvertBoolToStringModel):
class Config:
arbitrary_types_allowed = True
allow_population_by_field_name = True

server: List[Server] = Field(default=[])
authentication: List[Authentication] = Field(default=[])
trusted: List[int] = Field(default=[])
enable: Optional[bool] = Field(default=False)
stratum: Optional[int] = Field(default=None)
source: Optional[str] = Field(default=None)
authentication: List[Authentication] = Field(default=[], data_path=["keys"])
trusted: List[int] = Field(default=[], data_path=["keys"])
enable: Optional[bool] = Field(default=False, data_path=["master"])
stratum: Optional[int] = Field(default=None, data_path=["master"])
source: Optional[str] = Field(default=None, data_path=["master"])

payload_path: ClassVar[Path] = Path(__file__).parent / "DEPRECATED"
type: ClassVar[str] = "cisco_ntp"
12 changes: 8 additions & 4 deletions vmngclient/api/templates/models/cisco_omp_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,14 @@ class Config:
shutdown: Optional[bool]
omp_admin_distance_ipv4: Optional[int] = Field(alias="omp-admin-distance-ipv4")
omp_admin_distance_ipv6: Optional[int] = Field(alias="omp-admin-distance-ipv6")
advertisement_interval: Optional[int] = Field(DEFAULT_OMP_ADVERTISEMENT_INTERVAL, alias="advertisement-interval")
graceful_restart_timer: Optional[int] = Field(DEFAULT_OMP_GRACEFUL_RESTART_TIMER, alias="graceful-restart-timer")
eor_timer: Optional[int] = Field(DEFAULT_OMP_EOR_TIMER, alias="eor-timer")
holdtime: Optional[int] = DEFAULT_OMP_HOLDTIME
advertisement_interval: Optional[int] = Field(
DEFAULT_OMP_ADVERTISEMENT_INTERVAL, alias="advertisement-interval", data_path=["timers"]
)
graceful_restart_timer: Optional[int] = Field(
DEFAULT_OMP_GRACEFUL_RESTART_TIMER, alias="graceful-restart-timer", data_path=["timers"]
)
eor_timer: Optional[int] = Field(DEFAULT_OMP_EOR_TIMER, alias="eor-timer", data_path=["timers"])
holdtime: Optional[int] = Field(DEFAULT_OMP_HOLDTIME, data_path=["timers"])
advertise: Optional[List[IPv4Advertise]]
ipv6_advertise: Optional[List[IPv6Advertise]] = Field(alias="ipv6-advertise")
ignore_region_path_length: Optional[bool] = Field(False, alias="ignore-region-path-length")
Expand Down
Loading

0 comments on commit 722cbbd

Please sign in to comment.