sysbot 0.2.2__tar.gz → 0.2.3__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.
- {sysbot-0.2.2 → sysbot-0.2.3}/PKG-INFO +1 -2
- {sysbot-0.2.2 → sysbot-0.2.3}/setup.py +0 -1
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/_version.py +3 -3
- sysbot-0.2.3/sysbot/modules/container/__init__.py +7 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/modules/linux/dnf.py +21 -5
- sysbot-0.2.3/sysbot/modules/virtualization/__init__.py +7 -0
- sysbot-0.2.3/sysbot/modules/virtualization/harvester.py +381 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/utils/engine.py +107 -2
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot.egg-info/PKG-INFO +1 -2
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot.egg-info/SOURCES.txt +10 -7
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot.egg-info/requires.txt +0 -1
- {sysbot-0.2.2 → sysbot-0.2.3}/.github/workflows/docs.yml +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/.github/workflows/pypi-publish.yml +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/.gitignore +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/CONTRIBUTING.md +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/LICENSE +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/MANIFEST.in +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/README.md +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/pyproject.toml +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/setup.cfg +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/README.md +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/Sysbot.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/__init__.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/connectors/__init__.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/connectors/http.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/connectors/local.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/connectors/socket.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/connectors/ssh.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/connectors/winrm.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/modules/__init__.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/modules/bmc/__init__.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/modules/bmc/idrac.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/modules/bmc/ilo.py +0 -0
- {sysbot-0.2.2/sysbot/modules/linux → sysbot-0.2.3/sysbot/modules/container}/kubernetes.py +0 -0
- {sysbot-0.2.2/sysbot/modules/linux → sysbot-0.2.3/sysbot/modules/container}/podman.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/modules/linux/__init__.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/modules/linux/file.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/modules/linux/firewalld.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/modules/linux/ip.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/modules/linux/iptables.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/modules/linux/process.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/modules/linux/rpm.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/modules/linux/selinux.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/modules/linux/sysinfo.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/modules/linux/systemd.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/modules/linux/users.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/modules/monitoring/__init__.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/modules/monitoring/grafana.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/modules/network/__init__.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/modules/network/cisco/__init__.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/modules/network/cisco/catalyst.py +0 -0
- {sysbot-0.2.2/sysbot/modules/linux → sysbot-0.2.3/sysbot/modules/virtualization}/libvirt.py +0 -0
- {sysbot-0.2.2/sysbot/modules → sysbot-0.2.3/sysbot/modules/virtualization}/vmware/__init__.py +0 -0
- {sysbot-0.2.2/sysbot/modules → sysbot-0.2.3/sysbot/modules/virtualization}/vmware/nsx.py +0 -0
- {sysbot-0.2.2/sysbot/modules → sysbot-0.2.3/sysbot/modules/virtualization}/vmware/sddcmanager.py +0 -0
- {sysbot-0.2.2/sysbot/modules → sysbot-0.2.3/sysbot/modules/virtualization}/vmware/vsphere.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/modules/windows/__init__.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/modules/windows/adcs.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/modules/windows/adds.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/modules/windows/dnsserver.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/modules/windows/file.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/modules/windows/firewall.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/modules/windows/ip.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/modules/windows/sysinfo.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/modules/windows/users.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/modules/windows/veeam.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/modules/windows/wsus.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/plugins/__init__.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/plugins/ansible.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/plugins/data.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/plugins/vault.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/utils/__init__.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/utils/helper.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/utils/robot/__init__.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/utils/robot/listener/__init__.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/utils/robot/listener/mongodb.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/utils/robot/listener/mysql.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/utils/robot/listener/postgresql.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/utils/robot/listener/sqlite.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot/utils/robot/polarion.py +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot.egg-info/dependency_links.txt +0 -0
- {sysbot-0.2.2 → sysbot-0.2.3}/sysbot.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sysbot
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: System test automation library with support for SSH, WinRM, HTTP, databases, and Robot Framework integration
|
|
5
5
|
Home-page: https://github.com/JoReci2/sysbot
|
|
6
6
|
Author: Thibault SCIRE
|
|
@@ -33,7 +33,6 @@ Description-Content-Type: text/markdown
|
|
|
33
33
|
License-File: LICENSE
|
|
34
34
|
Requires-Dist: robotframework
|
|
35
35
|
Requires-Dist: paramiko
|
|
36
|
-
Requires-Dist: sshtunnel
|
|
37
36
|
Requires-Dist: netmiko
|
|
38
37
|
Requires-Dist: redfish
|
|
39
38
|
Requires-Dist: pywinrm
|
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.2.
|
|
32
|
-
__version_tuple__ = version_tuple = (0, 2,
|
|
31
|
+
__version__ = version = '0.2.3'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 2, 3)
|
|
33
33
|
|
|
34
|
-
__commit_id__ = commit_id = '
|
|
34
|
+
__commit_id__ = commit_id = 'gaa9194bbf'
|
|
@@ -6,7 +6,6 @@ RHEL/Fedora-based Linux systems, including repository management and package
|
|
|
6
6
|
operations.
|
|
7
7
|
"""
|
|
8
8
|
from sysbot.utils.engine import ComponentBase
|
|
9
|
-
import json
|
|
10
9
|
import configparser
|
|
11
10
|
from io import StringIO
|
|
12
11
|
|
|
@@ -14,7 +13,7 @@ from io import StringIO
|
|
|
14
13
|
class Dnf(ComponentBase):
|
|
15
14
|
"""DNF package manager operations class for RHEL/Fedora-based systems."""
|
|
16
15
|
|
|
17
|
-
def repolist(self, alias: str, **kwargs) ->
|
|
16
|
+
def repolist(self, alias: str, **kwargs) -> list:
|
|
18
17
|
"""
|
|
19
18
|
Get list of DNF repositories.
|
|
20
19
|
|
|
@@ -23,10 +22,27 @@ class Dnf(ComponentBase):
|
|
|
23
22
|
**kwargs: Additional command execution options.
|
|
24
23
|
|
|
25
24
|
Returns:
|
|
26
|
-
|
|
25
|
+
List of dictionaries containing repository information,
|
|
26
|
+
each with 'id' and 'name' keys.
|
|
27
27
|
"""
|
|
28
|
-
output = self.execute_command(alias, "dnf repolist
|
|
29
|
-
|
|
28
|
+
output = self.execute_command(alias, "dnf repolist", **kwargs)
|
|
29
|
+
repos = []
|
|
30
|
+
# Locate the header line (e.g. "repo id repo name") and parse
|
|
31
|
+
# only the lines that follow it, skipping any metadata lines printed
|
|
32
|
+
# before the table (e.g. "Last metadata expiration check: …").
|
|
33
|
+
header_found = False
|
|
34
|
+
for line in output.splitlines():
|
|
35
|
+
if not header_found:
|
|
36
|
+
if line.lstrip().lower().startswith("repo id"):
|
|
37
|
+
header_found = True
|
|
38
|
+
continue
|
|
39
|
+
line = line.strip()
|
|
40
|
+
if not line:
|
|
41
|
+
continue
|
|
42
|
+
parts = line.split(None, 1)
|
|
43
|
+
if parts:
|
|
44
|
+
repos.append({"id": parts[0], "name": parts[1].strip() if len(parts) > 1 else ""})
|
|
45
|
+
return repos
|
|
30
46
|
|
|
31
47
|
def repofile(self, alias: str, file: str, **kwargs) -> dict:
|
|
32
48
|
"""
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Harvester Module
|
|
3
|
+
|
|
4
|
+
This module provides methods for interacting with Rancher Harvester HCI (Hyper-Converged Infrastructure)
|
|
5
|
+
via REST API. It supports operations such as virtual machine management, image management,
|
|
6
|
+
volume management, and cluster information retrieval.
|
|
7
|
+
|
|
8
|
+
Note: This module uses the HTTP connector with API Key or Basic authentication.
|
|
9
|
+
Sessions should be opened with protocol="http" and product="apikey" or "basicauth".
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
from sysbot.utils.engine import ComponentBase
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Harvester(ComponentBase):
|
|
17
|
+
"""
|
|
18
|
+
Harvester module for hyper-converged infrastructure management.
|
|
19
|
+
|
|
20
|
+
This class provides methods to interact with Rancher Harvester systems using REST API
|
|
21
|
+
via the HTTP connector with API Key or Basic authentication.
|
|
22
|
+
All methods require an alias to identify the established HTTP session.
|
|
23
|
+
|
|
24
|
+
Example:
|
|
25
|
+
Basic usage with API key authentication::
|
|
26
|
+
|
|
27
|
+
import sysbot
|
|
28
|
+
|
|
29
|
+
bot = sysbot.Sysbot(['modules.virtualization.harvester'])
|
|
30
|
+
|
|
31
|
+
# Open session with API key
|
|
32
|
+
bot.open_session(
|
|
33
|
+
"harvester",
|
|
34
|
+
"http",
|
|
35
|
+
"apikey",
|
|
36
|
+
"harvester.example.com",
|
|
37
|
+
443,
|
|
38
|
+
apikey="your-api-key-here"
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
# Get cluster version
|
|
42
|
+
version = bot.modules.virtualization.harvester.get_version("harvester")
|
|
43
|
+
|
|
44
|
+
# List virtual machines
|
|
45
|
+
vms = bot.modules.virtualization.harvester.list_virtual_machines("harvester")
|
|
46
|
+
|
|
47
|
+
# Get specific VM
|
|
48
|
+
vm = bot.modules.virtualization.harvester.get_virtual_machine("harvester", "my-vm")
|
|
49
|
+
|
|
50
|
+
bot.close_session("harvester")
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def _parse_response(self, response):
|
|
54
|
+
"""
|
|
55
|
+
Parse HTTP response and decode JSON.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
response: The raw HTTP response bytes.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Parsed JSON object (dict or list).
|
|
62
|
+
"""
|
|
63
|
+
return json.loads(response.decode())
|
|
64
|
+
|
|
65
|
+
def get_version(self, alias: str, **kwargs) -> dict:
|
|
66
|
+
"""
|
|
67
|
+
Get Harvester version information.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
alias (str): The session alias for the Harvester connection.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
dict: Version information.
|
|
74
|
+
"""
|
|
75
|
+
response = self.execute_command(alias, "/v1/harvesterhci.io.settings/server-version", options={"method": "GET"}, **kwargs)
|
|
76
|
+
return self._parse_response(response)
|
|
77
|
+
|
|
78
|
+
def list_virtual_machines(self, alias: str, namespace: str = "default", **kwargs) -> dict:
|
|
79
|
+
"""
|
|
80
|
+
List all virtual machines in a namespace.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
alias (str): The session alias for the Harvester connection.
|
|
84
|
+
namespace (str): Kubernetes namespace (default: "default").
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
dict: List of virtual machines.
|
|
88
|
+
"""
|
|
89
|
+
response = self.execute_command(
|
|
90
|
+
alias,
|
|
91
|
+
f"/apis/kubevirt.io/v1/namespaces/{namespace}/virtualmachines",
|
|
92
|
+
options={"method": "GET"},
|
|
93
|
+
**kwargs
|
|
94
|
+
)
|
|
95
|
+
return self._parse_response(response)
|
|
96
|
+
|
|
97
|
+
def get_virtual_machine(self, alias: str, name: str, namespace: str = "default", **kwargs) -> dict:
|
|
98
|
+
"""
|
|
99
|
+
Get details of a specific virtual machine.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
alias (str): The session alias for the Harvester connection.
|
|
103
|
+
name (str): Virtual machine name.
|
|
104
|
+
namespace (str): Kubernetes namespace (default: "default").
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
dict: Virtual machine details.
|
|
108
|
+
"""
|
|
109
|
+
response = self.execute_command(
|
|
110
|
+
alias,
|
|
111
|
+
f"/apis/kubevirt.io/v1/namespaces/{namespace}/virtualmachines/{name}",
|
|
112
|
+
options={"method": "GET"},
|
|
113
|
+
**kwargs
|
|
114
|
+
)
|
|
115
|
+
return self._parse_response(response)
|
|
116
|
+
|
|
117
|
+
def list_vm_instances(self, alias: str, namespace: str = "default", **kwargs) -> dict:
|
|
118
|
+
"""
|
|
119
|
+
List all virtual machine instances in a namespace.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
alias (str): The session alias for the Harvester connection.
|
|
123
|
+
namespace (str): Kubernetes namespace (default: "default").
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
dict: List of virtual machine instances.
|
|
127
|
+
"""
|
|
128
|
+
response = self.execute_command(
|
|
129
|
+
alias,
|
|
130
|
+
f"/apis/kubevirt.io/v1/namespaces/{namespace}/virtualmachineinstances",
|
|
131
|
+
options={"method": "GET"},
|
|
132
|
+
**kwargs
|
|
133
|
+
)
|
|
134
|
+
return self._parse_response(response)
|
|
135
|
+
|
|
136
|
+
def get_vm_instance(self, alias: str, name: str, namespace: str = "default", **kwargs) -> dict:
|
|
137
|
+
"""
|
|
138
|
+
Get details of a specific virtual machine instance.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
alias (str): The session alias for the Harvester connection.
|
|
142
|
+
name (str): Virtual machine instance name.
|
|
143
|
+
namespace (str): Kubernetes namespace (default: "default").
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
dict: Virtual machine instance details.
|
|
147
|
+
"""
|
|
148
|
+
response = self.execute_command(
|
|
149
|
+
alias,
|
|
150
|
+
f"/apis/kubevirt.io/v1/namespaces/{namespace}/virtualmachineinstances/{name}",
|
|
151
|
+
options={"method": "GET"},
|
|
152
|
+
**kwargs
|
|
153
|
+
)
|
|
154
|
+
return self._parse_response(response)
|
|
155
|
+
|
|
156
|
+
def list_images(self, alias: str, namespace: str = "default", **kwargs) -> dict:
|
|
157
|
+
"""
|
|
158
|
+
List all VM images in a namespace.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
alias (str): The session alias for the Harvester connection.
|
|
162
|
+
namespace (str): Kubernetes namespace (default: "default").
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
dict: List of VM images.
|
|
166
|
+
"""
|
|
167
|
+
response = self.execute_command(
|
|
168
|
+
alias,
|
|
169
|
+
f"/apis/harvesterhci.io/v1beta1/namespaces/{namespace}/virtualmachineimages",
|
|
170
|
+
options={"method": "GET"},
|
|
171
|
+
**kwargs
|
|
172
|
+
)
|
|
173
|
+
return self._parse_response(response)
|
|
174
|
+
|
|
175
|
+
def get_image(self, alias: str, name: str, namespace: str = "default", **kwargs) -> dict:
|
|
176
|
+
"""
|
|
177
|
+
Get details of a specific VM image.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
alias (str): The session alias for the Harvester connection.
|
|
181
|
+
name (str): Image name.
|
|
182
|
+
namespace (str): Kubernetes namespace (default: "default").
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
dict: Image details.
|
|
186
|
+
"""
|
|
187
|
+
response = self.execute_command(
|
|
188
|
+
alias,
|
|
189
|
+
f"/apis/harvesterhci.io/v1beta1/namespaces/{namespace}/virtualmachineimages/{name}",
|
|
190
|
+
options={"method": "GET"},
|
|
191
|
+
**kwargs
|
|
192
|
+
)
|
|
193
|
+
return self._parse_response(response)
|
|
194
|
+
|
|
195
|
+
def list_volume_claims(self, alias: str, namespace: str = "default", **kwargs) -> dict:
|
|
196
|
+
"""
|
|
197
|
+
List all persistent volume claims in a namespace.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
alias (str): The session alias for the Harvester connection.
|
|
201
|
+
namespace (str): Kubernetes namespace (default: "default").
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
dict: List of persistent volume claims.
|
|
205
|
+
"""
|
|
206
|
+
response = self.execute_command(
|
|
207
|
+
alias,
|
|
208
|
+
f"/api/v1/namespaces/{namespace}/persistentvolumeclaims",
|
|
209
|
+
options={"method": "GET"},
|
|
210
|
+
**kwargs
|
|
211
|
+
)
|
|
212
|
+
return self._parse_response(response)
|
|
213
|
+
|
|
214
|
+
def get_volume_claim(self, alias: str, name: str, namespace: str = "default", **kwargs) -> dict:
|
|
215
|
+
"""
|
|
216
|
+
Get details of a specific persistent volume claim.
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
alias (str): The session alias for the Harvester connection.
|
|
220
|
+
name (str): Volume claim name.
|
|
221
|
+
namespace (str): Kubernetes namespace (default: "default").
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
dict: Volume claim details.
|
|
225
|
+
"""
|
|
226
|
+
response = self.execute_command(
|
|
227
|
+
alias,
|
|
228
|
+
f"/api/v1/namespaces/{namespace}/persistentvolumeclaims/{name}",
|
|
229
|
+
options={"method": "GET"},
|
|
230
|
+
**kwargs
|
|
231
|
+
)
|
|
232
|
+
return self._parse_response(response)
|
|
233
|
+
|
|
234
|
+
def list_networks(self, alias: str, namespace: str = "default", **kwargs) -> dict:
|
|
235
|
+
"""
|
|
236
|
+
List all networks in a namespace.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
alias (str): The session alias for the Harvester connection.
|
|
240
|
+
namespace (str): Kubernetes namespace (default: "default").
|
|
241
|
+
|
|
242
|
+
Returns:
|
|
243
|
+
dict: List of networks.
|
|
244
|
+
"""
|
|
245
|
+
response = self.execute_command(
|
|
246
|
+
alias,
|
|
247
|
+
f"/apis/k8s.cni.cncf.io/v1/namespaces/{namespace}/network-attachment-definitions",
|
|
248
|
+
options={"method": "GET"},
|
|
249
|
+
**kwargs
|
|
250
|
+
)
|
|
251
|
+
return self._parse_response(response)
|
|
252
|
+
|
|
253
|
+
def get_network(self, alias: str, name: str, namespace: str = "default", **kwargs) -> dict:
|
|
254
|
+
"""
|
|
255
|
+
Get details of a specific network.
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
alias (str): The session alias for the Harvester connection.
|
|
259
|
+
name (str): Network name.
|
|
260
|
+
namespace (str): Kubernetes namespace (default: "default").
|
|
261
|
+
|
|
262
|
+
Returns:
|
|
263
|
+
dict: Network details.
|
|
264
|
+
"""
|
|
265
|
+
response = self.execute_command(
|
|
266
|
+
alias,
|
|
267
|
+
f"/apis/k8s.cni.cncf.io/v1/namespaces/{namespace}/network-attachment-definitions/{name}",
|
|
268
|
+
options={"method": "GET"},
|
|
269
|
+
**kwargs
|
|
270
|
+
)
|
|
271
|
+
return self._parse_response(response)
|
|
272
|
+
|
|
273
|
+
def list_nodes(self, alias: str, **kwargs) -> dict:
|
|
274
|
+
"""
|
|
275
|
+
List all cluster nodes.
|
|
276
|
+
|
|
277
|
+
Args:
|
|
278
|
+
alias (str): The session alias for the Harvester connection.
|
|
279
|
+
|
|
280
|
+
Returns:
|
|
281
|
+
dict: List of cluster nodes.
|
|
282
|
+
"""
|
|
283
|
+
response = self.execute_command(
|
|
284
|
+
alias,
|
|
285
|
+
"/api/v1/nodes",
|
|
286
|
+
options={"method": "GET"},
|
|
287
|
+
**kwargs
|
|
288
|
+
)
|
|
289
|
+
return self._parse_response(response)
|
|
290
|
+
|
|
291
|
+
def get_node(self, alias: str, name: str, **kwargs) -> dict:
|
|
292
|
+
"""
|
|
293
|
+
Get details of a specific cluster node.
|
|
294
|
+
|
|
295
|
+
Args:
|
|
296
|
+
alias (str): The session alias for the Harvester connection.
|
|
297
|
+
name (str): Node name.
|
|
298
|
+
|
|
299
|
+
Returns:
|
|
300
|
+
dict: Node details.
|
|
301
|
+
"""
|
|
302
|
+
response = self.execute_command(
|
|
303
|
+
alias,
|
|
304
|
+
f"/api/v1/nodes/{name}",
|
|
305
|
+
options={"method": "GET"},
|
|
306
|
+
**kwargs
|
|
307
|
+
)
|
|
308
|
+
return self._parse_response(response)
|
|
309
|
+
|
|
310
|
+
def list_namespaces(self, alias: str, **kwargs) -> dict:
|
|
311
|
+
"""
|
|
312
|
+
List all namespaces in the cluster.
|
|
313
|
+
|
|
314
|
+
Args:
|
|
315
|
+
alias (str): The session alias for the Harvester connection.
|
|
316
|
+
|
|
317
|
+
Returns:
|
|
318
|
+
dict: List of namespaces.
|
|
319
|
+
"""
|
|
320
|
+
response = self.execute_command(
|
|
321
|
+
alias,
|
|
322
|
+
"/api/v1/namespaces",
|
|
323
|
+
options={"method": "GET"},
|
|
324
|
+
**kwargs
|
|
325
|
+
)
|
|
326
|
+
return self._parse_response(response)
|
|
327
|
+
|
|
328
|
+
def get_cluster_info(self, alias: str, **kwargs) -> dict:
|
|
329
|
+
"""
|
|
330
|
+
Get cluster information.
|
|
331
|
+
|
|
332
|
+
Args:
|
|
333
|
+
alias (str): The session alias for the Harvester connection.
|
|
334
|
+
|
|
335
|
+
Returns:
|
|
336
|
+
dict: Cluster information.
|
|
337
|
+
"""
|
|
338
|
+
response = self.execute_command(
|
|
339
|
+
alias,
|
|
340
|
+
"/api/v1",
|
|
341
|
+
options={"method": "GET"},
|
|
342
|
+
**kwargs
|
|
343
|
+
)
|
|
344
|
+
return self._parse_response(response)
|
|
345
|
+
|
|
346
|
+
def list_settings(self, alias: str, **kwargs) -> dict:
|
|
347
|
+
"""
|
|
348
|
+
List all Harvester settings.
|
|
349
|
+
|
|
350
|
+
Args:
|
|
351
|
+
alias (str): The session alias for the Harvester connection.
|
|
352
|
+
|
|
353
|
+
Returns:
|
|
354
|
+
dict: List of Harvester settings.
|
|
355
|
+
"""
|
|
356
|
+
response = self.execute_command(
|
|
357
|
+
alias,
|
|
358
|
+
"/v1/harvesterhci.io.settings",
|
|
359
|
+
options={"method": "GET"},
|
|
360
|
+
**kwargs
|
|
361
|
+
)
|
|
362
|
+
return self._parse_response(response)
|
|
363
|
+
|
|
364
|
+
def get_setting(self, alias: str, name: str, **kwargs) -> dict:
|
|
365
|
+
"""
|
|
366
|
+
Get a specific Harvester setting.
|
|
367
|
+
|
|
368
|
+
Args:
|
|
369
|
+
alias (str): The session alias for the Harvester connection.
|
|
370
|
+
name (str): Setting name.
|
|
371
|
+
|
|
372
|
+
Returns:
|
|
373
|
+
dict: Setting details.
|
|
374
|
+
"""
|
|
375
|
+
response = self.execute_command(
|
|
376
|
+
alias,
|
|
377
|
+
f"/v1/harvesterhci.io.settings/{name}",
|
|
378
|
+
options={"method": "GET"},
|
|
379
|
+
**kwargs
|
|
380
|
+
)
|
|
381
|
+
return self._parse_response(response)
|
|
@@ -26,13 +26,118 @@ import base64
|
|
|
26
26
|
import os
|
|
27
27
|
import json
|
|
28
28
|
import importlib
|
|
29
|
-
|
|
29
|
+
import select
|
|
30
|
+
import socket
|
|
31
|
+
import threading
|
|
32
|
+
import paramiko
|
|
30
33
|
from abc import ABC, abstractmethod
|
|
31
34
|
from pathlib import Path
|
|
32
35
|
from typing import Any, Dict, Optional, Union, List
|
|
33
36
|
from cryptography.fernet import Fernet
|
|
34
37
|
|
|
35
38
|
|
|
39
|
+
class SSHTunnel:
|
|
40
|
+
"""
|
|
41
|
+
A lightweight SSH tunnel implementation using paramiko, replacing the
|
|
42
|
+
unmaintained sshtunnel library which is incompatible with paramiko >= 3.x
|
|
43
|
+
(paramiko removed DSSKey in version 3.0).
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(
|
|
47
|
+
self,
|
|
48
|
+
ssh_address_or_host,
|
|
49
|
+
remote_bind_address,
|
|
50
|
+
ssh_username=None,
|
|
51
|
+
ssh_password=None,
|
|
52
|
+
):
|
|
53
|
+
self._ssh_host, self._ssh_port = ssh_address_or_host
|
|
54
|
+
self._remote_host, self._remote_port = remote_bind_address
|
|
55
|
+
self._ssh_username = ssh_username
|
|
56
|
+
self._ssh_password = ssh_password
|
|
57
|
+
self._transport = None
|
|
58
|
+
self._server_socket = None
|
|
59
|
+
self._local_bind_port = None
|
|
60
|
+
self._stop_event = threading.Event()
|
|
61
|
+
self._accept_thread = None
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def local_bind_port(self):
|
|
65
|
+
return self._local_bind_port
|
|
66
|
+
|
|
67
|
+
def start(self):
|
|
68
|
+
self._transport = paramiko.Transport((self._ssh_host, self._ssh_port))
|
|
69
|
+
self._transport.connect(
|
|
70
|
+
username=self._ssh_username, password=self._ssh_password
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
self._server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
74
|
+
self._server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
75
|
+
self._server_socket.bind(("127.0.0.1", 0))
|
|
76
|
+
self._local_bind_port = self._server_socket.getsockname()[1]
|
|
77
|
+
self._server_socket.listen(5)
|
|
78
|
+
|
|
79
|
+
self._accept_thread = threading.Thread(
|
|
80
|
+
target=self._accept_loop, daemon=True
|
|
81
|
+
)
|
|
82
|
+
self._accept_thread.start()
|
|
83
|
+
|
|
84
|
+
def _accept_loop(self):
|
|
85
|
+
while not self._stop_event.is_set():
|
|
86
|
+
self._server_socket.settimeout(1.0)
|
|
87
|
+
try:
|
|
88
|
+
client_sock, addr = self._server_socket.accept()
|
|
89
|
+
except socket.timeout:
|
|
90
|
+
continue
|
|
91
|
+
except OSError:
|
|
92
|
+
break
|
|
93
|
+
try:
|
|
94
|
+
channel = self._transport.open_channel(
|
|
95
|
+
"direct-tcpip",
|
|
96
|
+
(self._remote_host, self._remote_port),
|
|
97
|
+
addr,
|
|
98
|
+
)
|
|
99
|
+
except paramiko.SSHException:
|
|
100
|
+
client_sock.close()
|
|
101
|
+
continue
|
|
102
|
+
t = threading.Thread(
|
|
103
|
+
target=self._forward_data,
|
|
104
|
+
args=(client_sock, channel),
|
|
105
|
+
daemon=True,
|
|
106
|
+
)
|
|
107
|
+
t.start()
|
|
108
|
+
|
|
109
|
+
def _forward_data(self, client_sock, channel):
|
|
110
|
+
try:
|
|
111
|
+
while not self._stop_event.is_set():
|
|
112
|
+
r, _, _ = select.select([client_sock, channel], [], [], 1.0)
|
|
113
|
+
if client_sock in r:
|
|
114
|
+
data = client_sock.recv(4096)
|
|
115
|
+
if not data:
|
|
116
|
+
break
|
|
117
|
+
channel.sendall(data)
|
|
118
|
+
if channel in r:
|
|
119
|
+
data = channel.recv(4096)
|
|
120
|
+
if not data:
|
|
121
|
+
break
|
|
122
|
+
client_sock.sendall(data)
|
|
123
|
+
finally:
|
|
124
|
+
client_sock.close()
|
|
125
|
+
channel.close()
|
|
126
|
+
|
|
127
|
+
def stop(self):
|
|
128
|
+
self._stop_event.set()
|
|
129
|
+
if self._server_socket:
|
|
130
|
+
try:
|
|
131
|
+
self._server_socket.close()
|
|
132
|
+
except OSError:
|
|
133
|
+
pass
|
|
134
|
+
if self._transport:
|
|
135
|
+
try:
|
|
136
|
+
self._transport.close()
|
|
137
|
+
except paramiko.SSHException:
|
|
138
|
+
pass
|
|
139
|
+
|
|
140
|
+
|
|
36
141
|
class ConnectorInterface(ABC):
|
|
37
142
|
def __init__(self):
|
|
38
143
|
self._cache = None
|
|
@@ -205,7 +310,7 @@ class TunnelingManager:
|
|
|
205
310
|
int(tunnel_config[index + 1]["port"]),
|
|
206
311
|
)
|
|
207
312
|
)
|
|
208
|
-
tunnel =
|
|
313
|
+
tunnel = SSHTunnel(
|
|
209
314
|
ssh_address_or_host=ssh_address_or_host,
|
|
210
315
|
remote_bind_address=remote_bind_address,
|
|
211
316
|
ssh_username=config["username"],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sysbot
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: System test automation library with support for SSH, WinRM, HTTP, databases, and Robot Framework integration
|
|
5
5
|
Home-page: https://github.com/JoReci2/sysbot
|
|
6
6
|
Author: Thibault SCIRE
|
|
@@ -33,7 +33,6 @@ Description-Content-Type: text/markdown
|
|
|
33
33
|
License-File: LICENSE
|
|
34
34
|
Requires-Dist: robotframework
|
|
35
35
|
Requires-Dist: paramiko
|
|
36
|
-
Requires-Dist: sshtunnel
|
|
37
36
|
Requires-Dist: netmiko
|
|
38
37
|
Requires-Dist: redfish
|
|
39
38
|
Requires-Dist: pywinrm
|
|
@@ -26,15 +26,15 @@ sysbot/modules/__init__.py
|
|
|
26
26
|
sysbot/modules/bmc/__init__.py
|
|
27
27
|
sysbot/modules/bmc/idrac.py
|
|
28
28
|
sysbot/modules/bmc/ilo.py
|
|
29
|
+
sysbot/modules/container/__init__.py
|
|
30
|
+
sysbot/modules/container/kubernetes.py
|
|
31
|
+
sysbot/modules/container/podman.py
|
|
29
32
|
sysbot/modules/linux/__init__.py
|
|
30
33
|
sysbot/modules/linux/dnf.py
|
|
31
34
|
sysbot/modules/linux/file.py
|
|
32
35
|
sysbot/modules/linux/firewalld.py
|
|
33
36
|
sysbot/modules/linux/ip.py
|
|
34
37
|
sysbot/modules/linux/iptables.py
|
|
35
|
-
sysbot/modules/linux/kubernetes.py
|
|
36
|
-
sysbot/modules/linux/libvirt.py
|
|
37
|
-
sysbot/modules/linux/podman.py
|
|
38
38
|
sysbot/modules/linux/process.py
|
|
39
39
|
sysbot/modules/linux/rpm.py
|
|
40
40
|
sysbot/modules/linux/selinux.py
|
|
@@ -46,10 +46,13 @@ sysbot/modules/monitoring/grafana.py
|
|
|
46
46
|
sysbot/modules/network/__init__.py
|
|
47
47
|
sysbot/modules/network/cisco/__init__.py
|
|
48
48
|
sysbot/modules/network/cisco/catalyst.py
|
|
49
|
-
sysbot/modules/
|
|
50
|
-
sysbot/modules/
|
|
51
|
-
sysbot/modules/
|
|
52
|
-
sysbot/modules/vmware/
|
|
49
|
+
sysbot/modules/virtualization/__init__.py
|
|
50
|
+
sysbot/modules/virtualization/harvester.py
|
|
51
|
+
sysbot/modules/virtualization/libvirt.py
|
|
52
|
+
sysbot/modules/virtualization/vmware/__init__.py
|
|
53
|
+
sysbot/modules/virtualization/vmware/nsx.py
|
|
54
|
+
sysbot/modules/virtualization/vmware/sddcmanager.py
|
|
55
|
+
sysbot/modules/virtualization/vmware/vsphere.py
|
|
53
56
|
sysbot/modules/windows/__init__.py
|
|
54
57
|
sysbot/modules/windows/adcs.py
|
|
55
58
|
sysbot/modules/windows/adds.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sysbot-0.2.2/sysbot/modules → sysbot-0.2.3/sysbot/modules/virtualization}/vmware/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{sysbot-0.2.2/sysbot/modules → sysbot-0.2.3/sysbot/modules/virtualization}/vmware/sddcmanager.py
RENAMED
|
File without changes
|
{sysbot-0.2.2/sysbot/modules → sysbot-0.2.3/sysbot/modules/virtualization}/vmware/vsphere.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|