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

Commit

Permalink
Merge branch 'fr_template/rm/cisco_vpn' of https://github.com/CiscoDe…
Browse files Browse the repository at this point in the history
…vNet/vManage-client into fr_template/rm/cisco_vpn
  • Loading branch information
kagrski committed Sep 25, 2023
2 parents 69392fa + 658bb4d commit f7021ba
Show file tree
Hide file tree
Showing 51 changed files with 5,904 additions and 1,011 deletions.
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
[Add more in depth analysis of what changed, provide logs, examples of usage]

# Checklist:
- [ ] Make sure to run pre-commit before commiting changes
- [ ] Make sure to run pre-commit before committing changes
- [ ] Make sure all checks have passed
- [ ] PR description is clear and comprehensive
- [ ] Mentioned the issue that this PR solves (if applicable)
Expand Down
5 changes: 5 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,8 @@ repos:
language: system
types: [python]
fail_fast: true

- repo: https://github.com/Yelp/detect-secrets
rev: v1.4.0
hooks:
- id: detect-secrets
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
# vManage-client
<p align="center">
<a href="#"><img src="docs/vManage-client_LOGO.svg" alt="vManage-client logo" style="height:150px" />
</p>

[![Python-Supported](https://img.shields.io/static/v1?label=Python&logo=Python&color=3776AB&message=3.8%20|%203.9%20|%203.10%20|%203.11)](https://www.python.org/)

vManage client is a package for creating simple and parallel automatic requests via official vManageAPI. It is intended to serve as a multiple session handler (provider, provider as a tenant, tenant). The library is not dependent on environment which is being run in, you just need a connection to any vManage.
vManage client is a package for creating simple and parallel automatic requests via official vManage API. It is intended to serve as a multiple session handler (provider, provider as a tenant, tenant). The library is not dependent on environment which is being run in, you just need a connection to any vManage.

## Installation
```console
Expand Down
47 changes: 47 additions & 0 deletions docs/vManage-client_LOGO.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion vmngclient/api/admin_tech_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ def generate(
timeout=request_timeout,
)
except HTTPError as http_error:
response = http_error.response
response = http_error.response # type: ignore
if response.status_code == 200:
return response.json()["fileName"]
if response.status_code == 400 and create_admin_tech_error_msgs in response.json().get("error", {}).get(
Expand Down
52 changes: 25 additions & 27 deletions vmngclient/api/template_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from vmngclient.api.templates.models.cisco_ntp_model import CiscoNTPModel
from vmngclient.api.templates.models.cisco_omp_model import CiscoOMPModel
from vmngclient.api.templates.models.cisco_ospf import CiscoOSPFModel
from vmngclient.api.templates.models.cisco_ospfv3 import CiscoOspfv3Model
from vmngclient.api.templates.models.cisco_secure_internet_gateway import CiscoSecureInternetGatewayModel
from vmngclient.api.templates.models.cisco_snmp_model import CiscoSNMPModel
from vmngclient.api.templates.models.cisco_system import CiscoSystemModel
Expand Down Expand Up @@ -94,7 +95,10 @@ def get(self, template):
raise NotImplementedError()

def _get_feature_templates(
self, summary: bool = True, offset: Optional[int] = None, limit: Optional[int] = None
self,
summary: bool = True,
offset: Optional[int] = None,
limit: Optional[int] = None,
) -> DataSequence[FeatureTemplateInfo]:
"""In a multitenant vManage system, this API is only available in the Provider view."""
endpoint = "/dataservice/template/feature"
Expand Down Expand Up @@ -139,7 +143,11 @@ def _attach_feature(self, name: str, device: Device, **kwargs):
def get_device_specific_variables(name: str):
endpoint = "/dataservice/template/device/config/exportcsv"
template_id = self.get(DeviceTemplate).filter(name=name).single_or_default().id
body = {"templateId": template_id, "isEdited": False, "isMasterEdited": False}
body = {
"templateId": template_id,
"isEdited": False,
"isMasterEdited": False,
}

values = self.session.post(endpoint, json=body).json()["header"]["columns"]
return [DeviceSpecificValue(**value) for value in values]
Expand Down Expand Up @@ -454,7 +462,8 @@ def get_general_template_info(
return _template

def parse_general_template(
general_template: GeneralTemplate, fr_templates: DataSequence[FeatureTemplateInfo]
general_template: GeneralTemplate,
fr_templates: DataSequence[FeatureTemplateInfo],
) -> GeneralTemplate:
if general_template.subTemplates:
general_template.subTemplates = [
Expand Down Expand Up @@ -515,6 +524,7 @@ def is_created_by_generator(self, template: FeatureTemplate) -> bool:
CiscoOSPFModel,
CliTemplateModel,
CiscoSecureInternetGatewayModel,
CiscoOspfv3Model,
)

return isinstance(template, ported_templates)
Expand Down Expand Up @@ -559,32 +569,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", []) and ( # type: ignore
field.key == field_value.alias
or field.key == field_value.field_info.extra.get("vmanage_key") # type: ignore
):
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 Expand Up @@ -685,7 +678,12 @@ def edit_before_push(self, name: str, device: Device) -> bool:
error_details = json.loads(error.response.text)
logger.error(f"Error in config: {error_details['error']['details']}.")
return False
payload = {"templateId": template_id, "deviceIds": [device.uuid], "isEdited": True, "isMasterEdited": True}
payload = {
"templateId": template_id,
"deviceIds": [device.uuid],
"isEdited": True,
"isMasterEdited": True,
}
endpoint = "/dataservice/template/device/config/input/"
logger.info(f"Editing template: {name} of device: {device.hostname}.")
response = self.session.post(url=endpoint, json=payload).json()
Expand Down
4 changes: 3 additions & 1 deletion vmngclient/api/templates/feature_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,12 @@ def remove_device_variables(cls, values):
values["device_specific_variables"] = {}
to_delete = {}

# TODO: Add support for nested models with DeviceVariable
for key, value in values.items():
if isinstance(value, DeviceVariable):
to_delete[key] = value
values["device_specific_variables"][cls.__fields__[key].alias] = DeviceVariable(name=value.name)
field_key = cls.__fields__[key].field_info.extra.get("vmanage_key", cls.__fields__[key].alias)
values["device_specific_variables"][field_key] = DeviceVariable(name=value.name)

for var in to_delete:
if var in values:
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 or f.field_info.extra.get("vmanage_key") == 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)
Loading

0 comments on commit f7021ba

Please sign in to comment.