Skip to content
This repository was archived by the owner on Apr 30, 2025. It is now read-only.

Commit dfc3d90

Browse files
committed
[blueprints-provider] Implement blueprint-provider service worker
1 parent eaab4c4 commit dfc3d90

File tree

10 files changed

+1674
-0
lines changed

10 files changed

+1674
-0
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# 1.0.0
2+
- Add Create blueprint worker
3+
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Conductor worker for Frinx Uniconfig
2+
3+
4+
Conductor workers for Frinx blueprints-provider service
5+
6+
## Getting started
7+
8+
### Prerequisites
9+
10+
- Python 3.10+ is required to use this package.
11+
12+
### Install the package
13+
14+
```bash
15+
pip install frinx-blueprints-provider-worker
16+
```
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
## Release matrix
2+
3+
| Docker Image Tag | Python Library Version |
4+
|------------------|------------------------|
5+
6+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
version: '3.8'
2+
3+
services:
4+
postgres:
5+
image: postgres:16.4-alpine3.20
6+
user: postgres
7+
volumes:
8+
- /var/lib/postgresql/data
9+
command: postgres -N 300
10+
environment:
11+
- POSTGRES_USER=postgres
12+
- POSTGRES_PASSWORD=postgres
13+
- POSTGRES_DB=blueprints
14+
ports:
15+
- "5432:5432"
16+
healthcheck:
17+
test: [ "CMD-SHELL", "pg_isready -U postgres" ]
18+
interval: 10s
19+
timeout: 5s
20+
retries: 5
21+
start_period: 10s
22+
networks:
23+
- backend
24+
25+
networks:
26+
backend:

blueprints-provider/python/frinx_worker/__init__.py

Whitespace-only changes.

blueprints-provider/python/frinx_worker/blueprint_provider/__init__.py

Whitespace-only changes.
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import os
2+
from enum import Enum
3+
from types import MappingProxyType
4+
5+
import requests
6+
from frinx.common.conductor_enums import TaskResultStatus
7+
from frinx.common.type_aliases import DictAny
8+
from frinx.common.type_aliases import ListAny
9+
from frinx.common.worker.task_def import TaskOutput
10+
from frinx.common.worker.task_result import TaskResult
11+
from graphql_pydantic_converter.graphql_types import QueryForm
12+
from pydantic import BaseModel
13+
from pydantic import Field
14+
15+
# TODO: add to frinx-python-sdk
16+
BLUEPRINTS_PROVIDER_URL_BASE = os.getenv('BLUEPRINTS_PROVIDER_URL_BASE', 'http://localhost:8080/graphql')
17+
BLUEPRINTS_PROVIDER_HEADERS = MappingProxyType({'Content-Type': 'application/json'})
18+
19+
20+
class BlueprintsProviderWorkerOutput(TaskOutput):
21+
"""Blueprints-provider worker output."""
22+
23+
query: str = Field(
24+
description="Constructed GraphQL query.",
25+
)
26+
variable: DictAny | None = Field(
27+
description="Constructed input GraphQL variables.",
28+
default=None
29+
)
30+
response_body: ListAny | DictAny | None | None = Field(
31+
description="Response body.",
32+
)
33+
response_code: int = Field(
34+
description="Response code.",
35+
)
36+
37+
class ResponseStatus(str, Enum):
38+
"""Response status."""
39+
40+
DATA = "data"
41+
"""Response contains valid data."""
42+
ERRORS = "errors"
43+
"""Response contains some errors."""
44+
FAILED = "failed"
45+
"""HTTP request failed without providing list of errors in response."""
46+
47+
48+
class BlueprintsProviderOutput(BaseModel):
49+
"""Parsed response from Blueprints-provider service."""
50+
51+
status: ResponseStatus = Field(
52+
description="Response status."
53+
)
54+
code: int = Field(
55+
description="Parsed response code."
56+
)
57+
data: ListAny | DictAny | None = Field(
58+
default=None,
59+
description="Structured response data."
60+
)
61+
62+
63+
def execute_graphql_operation(
64+
query: str,
65+
variables: DictAny | None = None,
66+
blueprint_provider_url_base: str = BLUEPRINTS_PROVIDER_URL_BASE
67+
) -> BlueprintsProviderOutput:
68+
"""
69+
Execute GraphQL query.
70+
71+
:param query: GraphQL query
72+
:param variables: GraphQL variables in dictionary format
73+
:param blueprint_provider_url_base: Blueprints-provider service URL base
74+
:return: BlueprintsProviderOutput object
75+
""" ""
76+
response = requests.post(
77+
blueprint_provider_url_base,
78+
json={
79+
"query": query,
80+
"variables": variables
81+
},
82+
headers=BLUEPRINTS_PROVIDER_HEADERS
83+
)
84+
data = response.json()
85+
status_code = response.status_code
86+
87+
if data.get("errors") is not None:
88+
return BlueprintsProviderOutput(data=data["errors"], status=ResponseStatus.ERRORS, code=status_code)
89+
90+
if data.get("data") is not None:
91+
return BlueprintsProviderOutput(data=data["data"], status=ResponseStatus.DATA, code=status_code)
92+
93+
return BlueprintsProviderOutput(status=ResponseStatus.FAILED, code=status_code)
94+
95+
96+
def response_handler(query: QueryForm, response: BlueprintsProviderOutput) -> TaskResult:
97+
"""
98+
Handle response from Blueprints-service service.
99+
100+
:param query: GraphQL query information
101+
:param response: parsed topology-discovery response
102+
:return: built TaskResult object
103+
"""
104+
output = BlueprintsProviderWorkerOutput(
105+
response_code=response.code,
106+
response_body=response.data,
107+
query=query.query,
108+
variable=query.variable
109+
)
110+
match response.status:
111+
case ResponseStatus.DATA:
112+
task_result = TaskResult(status=TaskResultStatus.COMPLETED)
113+
task_result.status = TaskResultStatus.COMPLETED
114+
task_result.output = output
115+
return task_result
116+
case _:
117+
task_result = TaskResult(status=TaskResultStatus.FAILED)
118+
task_result.status = TaskResultStatus.FAILED
119+
task_result.logs = str(response)
120+
task_result.output = output
121+
return task_result
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
from frinx.common.type_aliases import ListStr
2+
from frinx.common.worker.service import ServiceWorkersImpl
3+
from frinx.common.worker.task_def import TaskDefinition
4+
from frinx.common.worker.task_def import TaskExecutionProperties
5+
from frinx.common.worker.task_def import TaskInput
6+
from frinx.common.worker.task_result import TaskResult
7+
from frinx.common.worker.worker import WorkerImpl
8+
from frinx_api.blueprints_provider import BlueprintNode
9+
from frinx_api.blueprints_provider import BlueprintNodeInput
10+
from frinx_api.blueprints_provider import BlueprintOutput
11+
from frinx_api.blueprints_provider import BlueprintType
12+
from frinx_api.blueprints_provider import ConnectionType
13+
from frinx_api.blueprints_provider import CreateBlueprintMutation
14+
from pydantic import Field
15+
16+
from frinx_worker.blueprint_provider.utils import BlueprintsProviderOutput
17+
from frinx_worker.blueprint_provider.utils import BlueprintsProviderWorkerOutput
18+
from frinx_worker.blueprint_provider.utils import execute_graphql_operation
19+
from frinx_worker.blueprint_provider.utils import response_handler
20+
21+
22+
class BlueprintsProviderWorkers(ServiceWorkersImpl):
23+
class CreateBlueprint(WorkerImpl):
24+
25+
class ExecutionProperties(TaskExecutionProperties):
26+
exclude_empty_inputs: bool = True
27+
28+
class WorkerDefinition(TaskDefinition):
29+
name: str = "BLUEPRINTS_PROVIDER_create_blueprint"
30+
description: str = "Create blueprint"
31+
labels: ListStr = ["BLUEPRINTS-PROVIDER"]
32+
timeout_seconds: int = 3600
33+
response_timeout_seconds: int = 3600
34+
35+
class WorkerInput(TaskInput):
36+
name: str = Field(
37+
description="Blueprint name",
38+
examples=["install_cli_cisco", "install_gnmi_nokia_7750_22_10"],
39+
)
40+
blueprint_type: BlueprintType = Field(
41+
description="Specifies the type of blueprint",
42+
examples=[BlueprintType.TOPOLOGY_LLDP],
43+
)
44+
connection_type: ConnectionType = Field(
45+
description="Management protocol used for creation of connection to the device",
46+
examples=[ConnectionType.NETCONF],
47+
)
48+
model_pattern: str | None = Field(
49+
description="Regular expression pattern for matching the model of the device",
50+
examples=["vsr .+"],
51+
)
52+
vendor_pattern: str | None = Field(
53+
description="Regular expression pattern for matching the vendor of the device",
54+
examples=[".*wrt"],
55+
)
56+
version_pattern: str | None = Field(
57+
description="Regular expression pattern for matching the version of the devices",
58+
examples=["v[0-9]+"],
59+
)
60+
template: str = Field(description="JSON string")
61+
62+
63+
class WorkerOutput(BlueprintsProviderWorkerOutput):
64+
...
65+
66+
def execute(self, worker_input: WorkerInput) -> TaskResult[WorkerOutput]:
67+
create_blueprint_mutation = CreateBlueprintMutation(
68+
node=BlueprintNodeInput(
69+
name=worker_input.name,
70+
blueprint_type=worker_input.blueprint_type,
71+
connection_type=worker_input.connection_type,
72+
model_pattern=worker_input.model_pattern,
73+
vendor_pattern=worker_input.vendor_pattern,
74+
version_pattern=worker_input.version_pattern,
75+
template=worker_input.template,
76+
),
77+
payload=BlueprintOutput(
78+
id=True,
79+
node=BlueprintNode(
80+
name=True,
81+
blueprint_type=True,
82+
connection_type=True,
83+
model_pattern=True,
84+
vendor_pattern=True,
85+
version_pattern=True,
86+
template=True,
87+
)
88+
)
89+
)
90+
91+
query = create_blueprint_mutation.render()
92+
response: BlueprintsProviderOutput = execute_graphql_operation(query=query.query, variables=query.variable)
93+
return response_handler(query, response)

0 commit comments

Comments
 (0)