acex-devkit 1.6.1__tar.gz → 1.7.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. {acex_devkit-1.6.1 → acex_devkit-1.7.0}/PKG-INFO +1 -1
  2. {acex_devkit-1.6.1 → acex_devkit-1.7.0}/pyproject.toml +1 -1
  3. {acex_devkit-1.6.1 → acex_devkit-1.7.0}/src/acex_devkit/drivers/base.py +21 -22
  4. {acex_devkit-1.6.1 → acex_devkit-1.7.0}/src/acex_devkit/drivers/base_driver.py +12 -7
  5. {acex_devkit-1.6.1 → acex_devkit-1.7.0}/src/acex_devkit/models/__init__.py +1 -0
  6. acex_devkit-1.7.0/src/acex_devkit/models/management_connection.py +16 -0
  7. {acex_devkit-1.6.1 → acex_devkit-1.7.0}/src/acex_devkit/models/node_response.py +22 -2
  8. {acex_devkit-1.6.1 → acex_devkit-1.7.0}/README.md +0 -0
  9. {acex_devkit-1.6.1 → acex_devkit-1.7.0}/src/acex_devkit/__init__.py +0 -0
  10. {acex_devkit-1.6.1 → acex_devkit-1.7.0}/src/acex_devkit/configdiffer/__init__.py +0 -0
  11. {acex_devkit-1.6.1 → acex_devkit-1.7.0}/src/acex_devkit/configdiffer/command.py +0 -0
  12. {acex_devkit-1.6.1 → acex_devkit-1.7.0}/src/acex_devkit/configdiffer/configdiffer.py +0 -0
  13. {acex_devkit-1.6.1 → acex_devkit-1.7.0}/src/acex_devkit/configdiffer/diff.py +0 -0
  14. {acex_devkit-1.6.1 → acex_devkit-1.7.0}/src/acex_devkit/configdiffer/old_configdiffer.py +0 -0
  15. {acex_devkit-1.6.1 → acex_devkit-1.7.0}/src/acex_devkit/configdiffer/old_diff.py +0 -0
  16. {acex_devkit-1.6.1 → acex_devkit-1.7.0}/src/acex_devkit/drivers/__init__.py +0 -0
  17. {acex_devkit-1.6.1 → acex_devkit-1.7.0}/src/acex_devkit/exceptions/__init__.py +0 -0
  18. {acex_devkit-1.6.1 → acex_devkit-1.7.0}/src/acex_devkit/models/acl_model.py +0 -0
  19. {acex_devkit-1.6.1 → acex_devkit-1.7.0}/src/acex_devkit/models/attribute_value.py +0 -0
  20. {acex_devkit-1.6.1 → acex_devkit-1.7.0}/src/acex_devkit/models/composed_configuration.py +0 -0
  21. {acex_devkit-1.6.1 → acex_devkit-1.7.0}/src/acex_devkit/models/container_entry.py +0 -0
  22. {acex_devkit-1.6.1 → acex_devkit-1.7.0}/src/acex_devkit/models/external_value.py +0 -0
  23. {acex_devkit-1.6.1 → acex_devkit-1.7.0}/src/acex_devkit/models/logging.py +0 -0
  24. {acex_devkit-1.6.1 → acex_devkit-1.7.0}/src/acex_devkit/models/ned.py +0 -0
  25. {acex_devkit-1.6.1 → acex_devkit-1.7.0}/src/acex_devkit/models/spanning_tree.py +0 -0
  26. {acex_devkit-1.6.1 → acex_devkit-1.7.0}/src/acex_devkit/types/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: acex-devkit
3
- Version: 1.6.1
3
+ Version: 1.7.0
4
4
  Summary: ACE-X DevKit - Development kit for building ACE-X drivers and plugins
5
5
  License: AGPL-3.0
6
6
  Keywords: automation,devkit,sdk,drivers,plugins
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "acex-devkit"
3
- version = "1.6.1"
3
+ version = "1.7.0"
4
4
  description = "ACE-X DevKit - Development kit for building ACE-X drivers and plugins"
5
5
  authors = ["Johan Lahti <johan.lahti@acebit.se>"]
6
6
  readme = "README.md"
@@ -3,6 +3,9 @@
3
3
  from abc import ABC, abstractmethod
4
4
  from typing import Any, Dict
5
5
 
6
+ from acex_devkit.models.node_response import NodeListItem
7
+ from acex_devkit.models.management_connection import ManagementConnection
8
+
6
9
 
7
10
  class ParserBase(ABC):
8
11
  """Base class for configuration parsers."""
@@ -38,36 +41,32 @@ class RendererBase(ABC):
38
41
 
39
42
 
40
43
  class TransportBase(ABC):
41
- """Base class for device transport/communication."""
42
-
43
- @abstractmethod
44
- def connect(self) -> None:
45
- """Establish connection to the device."""
46
- pass
44
+ """Base class for device transport/communication.
47
45
 
48
- @abstractmethod
49
- def send(self, payload: Any) -> None:
50
- """Send configuration to the device.
51
-
52
- Args:
53
- payload: Configuration payload to send
54
- """
55
- pass
46
+ Each method is self-contained — the driver decides internally
47
+ whether to open/close sessions per call, pool connections, or
48
+ make stateless requests.
49
+
50
+ Args:
51
+ node: The node instance (identity, hostname, vendor, os, ned_id)
52
+ connection: Management connection (target_ip, connection_type)
53
+ **kwargs: Future use (credentials, options, etc.)
54
+ """
56
55
 
57
56
  @abstractmethod
58
- def verify(self) -> bool:
59
- """Verify configuration was applied correctly.
60
-
61
- Returns:
62
- True if verification succeeded, False otherwise
63
- """
57
+ def get_config(self, node: NodeListItem, connection: ManagementConnection, **kwargs) -> str:
58
+ """Fetch the full running configuration from a device."""
64
59
  pass
65
60
 
66
61
  @abstractmethod
67
- def rollback(self) -> None:
68
- """Rollback configuration if verification fails."""
62
+ def send_config(self, node: NodeListItem, connection: ManagementConnection, commands: list[str], **kwargs) -> str:
63
+ """Apply configuration commands to a device."""
69
64
  pass
70
65
 
66
+ def execute(self, node: NodeListItem, connection: ManagementConnection, commands: list[str], **kwargs) -> list[str]:
67
+ """Run arbitrary commands and return output per command. Opt-in per driver."""
68
+ raise NotImplementedError(f"{self.__class__.__name__} does not implement execute()")
69
+
71
70
 
72
71
  class NetworkElementDriver:
73
72
  """Base class for network element drivers.
@@ -1,6 +1,9 @@
1
1
  from abc import ABC, abstractmethod
2
2
  from typing import Any, Dict
3
3
 
4
+ from acex_devkit.models.node_response import NodeListItem
5
+ from acex_devkit.models.management_connection import ManagementConnection
6
+
4
7
  class ParserBase(ABC):
5
8
  @abstractmethod
6
9
  def parse(self, model: Dict[str, Any]) -> Any:
@@ -14,16 +17,18 @@ class RendererBase(ABC):
14
17
 
15
18
  class TransportBase(ABC):
16
19
  @abstractmethod
17
- def connect(self) -> None: ...
18
-
19
- @abstractmethod
20
- def send(self, payload: Any) -> None: ...
20
+ def get_config(self, node: NodeListItem, connection: ManagementConnection, **kwargs) -> str:
21
+ """Fetch the full running configuration from a device."""
22
+ pass
21
23
 
22
24
  @abstractmethod
23
- def verify(self) -> bool: ...
25
+ def send_config(self, node: NodeListItem, connection: ManagementConnection, commands: list[str], **kwargs) -> str:
26
+ """Apply configuration commands to a device."""
27
+ pass
24
28
 
25
- @abstractmethod
26
- def rollback(self) -> None: ...
29
+ def execute(self, node: NodeListItem, connection: ManagementConnection, commands: list[str], **kwargs) -> list[str]:
30
+ """Run arbitrary commands. Opt-in per driver."""
31
+ raise NotImplementedError(f"{self.__class__.__name__} does not implement execute()")
27
32
 
28
33
  class NetworkElementDriver:
29
34
  """Kombinerar renderer + transport – exponeras som en plugin."""
@@ -6,6 +6,7 @@
6
6
  from .external_value import ExternalValue
7
7
  from .attribute_value import AttributeValue
8
8
  from .node_response import NodeResponse, NodeListItem, LogicalNodeResponse
9
+ from .management_connection import ManagementConnection, ConnectionType
9
10
 
10
11
  __all__ = [
11
12
  ExternalValue,
@@ -0,0 +1,16 @@
1
+ from pydantic import BaseModel
2
+ from typing import Optional
3
+ from enum import Enum
4
+
5
+
6
+ class ConnectionType(str, Enum):
7
+ ssh = "ssh"
8
+ telnet = "telnet"
9
+
10
+
11
+ class ManagementConnection(BaseModel):
12
+ id: Optional[int] = None
13
+ node_id: Optional[int] = None
14
+ primary: bool = True
15
+ connection_type: ConnectionType = ConnectionType.ssh
16
+ target_ip: Optional[str] = None
@@ -1,5 +1,5 @@
1
1
  from pydantic import BaseModel, Field
2
- from typing import Optional, Dict, Any
2
+ from typing import Annotated, Optional, Dict, Any, List, Literal, Union
3
3
  from enum import Enum
4
4
  from datetime import datetime
5
5
  from acex_devkit.models.composed_configuration import ComposedConfiguration
@@ -17,6 +17,7 @@ class LogicalNodeResponse(BaseModel):
17
17
 
18
18
  class Asset(BaseModel):
19
19
  id: Optional[int] = None
20
+ type: Literal["asset"] = "asset"
20
21
  vendor: Optional[str] = None
21
22
  serial_number: Optional[str] = None
22
23
  os: Optional[str] = None
@@ -25,6 +26,25 @@ class Asset(BaseModel):
25
26
  ned_id: Optional[str] = None
26
27
 
27
28
 
29
+ class AssetClusterAsset(BaseModel):
30
+ id: Optional[int] = None
31
+ vendor: Optional[str] = None
32
+ serial_number: Optional[str] = None
33
+ os: Optional[str] = None
34
+ os_version: Optional[str] = None
35
+ hardware_model: Optional[str] = None
36
+ ned_id: Optional[str] = None
37
+ cluster_index: Optional[int] = None
38
+
39
+
40
+ class AssetCluster(BaseModel):
41
+ id: Optional[int] = None
42
+ type: Literal["asset_cluster"] = "asset_cluster"
43
+ name: Optional[str] = None
44
+ ned_id: Optional[str] = None
45
+ assets: List[AssetClusterAsset] = []
46
+
47
+
28
48
  class AssetRefType(str, Enum):
29
49
  asset = "asset"
30
50
  asset_cluster = "asset_cluster"
@@ -65,7 +85,7 @@ class NodeListItem(NodeBase):
65
85
 
66
86
  class NodeResponse(NodeBase):
67
87
  id: Optional[int] = None
68
- asset: Asset
88
+ asset: Annotated[Union[Asset, AssetCluster], Field(discriminator="type")]
69
89
  logical_node: LogicalNodeResponse
70
90
  created_at: datetime
71
91
  updated_at: Optional[datetime] = None
File without changes