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

Commit

Permalink
dev: Fix Template API and remove jinja2 (#776)
Browse files Browse the repository at this point in the history
* template info use string as template type

* remove generation of Device template with jinja2

* catch non-existing template before attach operation
  • Loading branch information
cicharka authored Jul 25, 2024
1 parent 80b4c4b commit 6128555
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 57 deletions.
19 changes: 11 additions & 8 deletions catalystwan/api/template_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,14 @@ def _get_device_templates(
return self.session.endpoints.configuration_template_master.get_device_template_list(params=feature.value)

def attach(self, name: str, device: Device, timeout_seconds: int = 300, **kwargs):
template_type = self.get(DeviceTemplate).filter(name=name).single_or_default().config_type
if template_type == TemplateType.CLI:
template_info = self.get(DeviceTemplate).filter(name=name).single_or_default()
if not template_info:
raise TemplateNotFoundError(f"Template with name [{name}] does not exists.")

if template_info.config_type == TemplateType.CLI.value:
return self._attach_cli(name, device, timeout_seconds=timeout_seconds, **kwargs)

if template_type == TemplateType.FEATURE:
if template_info.config_type == TemplateType.FEATURE.value:
return self._attach_feature(name, device, timeout_seconds=timeout_seconds, **kwargs)

raise NotImplementedError()
Expand Down Expand Up @@ -495,18 +498,18 @@ def parse_general_template(
.id
)
payload = json.loads(device_template.generate_payload())
response = self.session.put(f"/dataservice/template/device/{template_id}", json=payload)
response = self.session.endpoints.configuration_template_master.edit_template(
template_id=template_id, payload=payload
)
return response
else:
# endpoint = "/dataservice/template/device/feature/"
# response = self.session.post(endpoint, json=payload)
payload = json.loads(device_template.generate_payload())
response = (
self.session.endpoints.configuration_template_master.create_device_template_from_feature_templates(
payload=payload
)
)

return response.text
return response.template_id

def is_created_by_generator(self, template: FeatureTemplate) -> bool:
"""Checks if template is created by generator
Expand Down
44 changes: 15 additions & 29 deletions catalystwan/api/templates/device_template/device_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@
from __future__ import annotations

import logging
from pathlib import Path
from typing import TYPE_CHECKING, ClassVar, List, Literal, Optional
from typing import TYPE_CHECKING, List, Literal, Optional
from uuid import UUID

from jinja2 import DebugUndefined, Environment, FileSystemLoader, meta # type: ignore
from pydantic import BaseModel, ConfigDict, Field, field_validator

if TYPE_CHECKING:
Expand Down Expand Up @@ -59,21 +57,19 @@ class DeviceTemplate(BaseModel):

template_name: str = Field(serialization_alias="templateName", validation_alias="templateName")
template_description: str = Field(serialization_alias="templateDescription", validation_alias="templateDescription")
general_templates: List[GeneralTemplate] = Field(
default=[], serialization_alias="generalTemplates", validation_alias="generalTemplates"
)
device_role: str = Field(default="sdwan-edge", serialization_alias="deviceRole", validation_alias="deviceRole")
device_type: str = Field(serialization_alias="deviceType", validation_alias="deviceType")
security_policy_id: str = Field(
default="", serialization_alias="securityPolicyId", validation_alias="securityPolicyId"
device_role: str = Field(default="sdwan-edge", serialization_alias="deviceRole", validation_alias="deviceRole")
config_type: Optional[str] = Field(
default="template", serialization_alias="configType", validation_alias="configType"
)
factory_default: Optional[bool] = Field(
default=False, serialization_alias="factoryDefault", validation_alias="factoryDefault"
)
policy_id: str = Field(default="", serialization_alias="policyId", validation_alias="policyId")
feature_template_uid_range: Optional[List] = Field(
default=[], serialization_alias="featureTemplateUidRange", validation_alias="featureTemplateUidRange"
)
config_type: Optional[str] = Field(
default="template", serialization_alias="configType", validation_alias="configType"
)

connection_preference_required: Optional[bool] = Field(
default=True,
serialization_alias="connectionPreferenceRequired",
Expand All @@ -82,8 +78,12 @@ class DeviceTemplate(BaseModel):
connection_preference: Optional[bool] = Field(
default=True, serialization_alias="connectionPreference", validation_alias="connectionPreference"
)
factory_default: Optional[bool] = Field(
default=False, serialization_alias="factoryDefault", validation_alias="factoryDefault"

general_templates: List[GeneralTemplate] = Field(
default=[], serialization_alias="generalTemplates", validation_alias="generalTemplates"
)
security_policy_id: str = Field(
default="", serialization_alias="securityPolicyId", validation_alias="securityPolicyId"
)

def get_security_policy_uuid(self) -> Optional[UUID]:
Expand All @@ -93,19 +93,7 @@ def get_policy_uuid(self) -> Optional[UUID]:
return str_to_uuid(self.policy_id)

def generate_payload(self) -> str:
env = Environment(
loader=FileSystemLoader(self.payload_path.parent),
trim_blocks=True,
lstrip_blocks=True,
undefined=DebugUndefined,
)
template = env.get_template(self.payload_path.name)
output = template.render(self.model_dump())

ast = env.parse(output)
if meta.find_undeclared_variables(ast):
logger.info(meta.find_undeclared_variables(ast))
raise Exception("There are undeclared variables.")
output = self.model_dump_json(by_alias=True)
return output

@field_validator("general_templates", mode="before")
Expand All @@ -119,8 +107,6 @@ def parse_templates(cls, value):
output.append(template)
return output

payload_path: ClassVar[Path] = Path(__file__).parent / "device_template_payload.json.j2"

@classmethod
def get(self, name: str, session: ManagerSession) -> DeviceTemplate:
device_template = session.api.templates.get(DeviceTemplate).filter(name=name).single_or_default()
Expand Down

This file was deleted.

15 changes: 14 additions & 1 deletion catalystwan/endpoints/configuration_template_master.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

# mypy: disable-error-code="empty-body"

from typing import List, Optional
from uuid import UUID

from pydantic import BaseModel, ConfigDict, Field

from catalystwan.endpoints import JSON, APIEndpoints, get, post
from catalystwan.endpoints import JSON, APIEndpoints, get, post, put
from catalystwan.models.templates import DeviceTemplateInformation, FeatureType
from catalystwan.typed_list import DataSequence

Expand All @@ -17,6 +18,14 @@ class TemplateID(BaseModel):
template_id: UUID = Field(serialization_alias="templateId", validation_alias="templateId")


class AttachedDevices(BaseModel):
model_config = ConfigDict(populate_by_name=True)

attached_devices: Optional[List] = Field(
default=[], serialization_alias="AttachedDevices", validation_alias="AttachedDevices"
)


class FeatureQueryParams(BaseModel):
model_config = ConfigDict(populate_by_name=True)

Expand All @@ -33,3 +42,7 @@ def get_device_template_list(
@post("/template/device/feature")
def create_device_template_from_feature_templates(self, payload: JSON) -> TemplateID:
...

@put("/template/device/{template_id}", "data")
def edit_template(self, template_id: str, payload: JSON) -> AttachedDevices:
...

0 comments on commit 6128555

Please sign in to comment.