vmware-storage 1.2.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.
- vmware_storage-1.2.0/.gitignore +13 -0
- vmware_storage-1.2.0/Dockerfile +13 -0
- vmware_storage-1.2.0/LICENSE +21 -0
- vmware_storage-1.2.0/PKG-INFO +148 -0
- vmware_storage-1.2.0/README.md +128 -0
- vmware_storage-1.2.0/RELEASE_NOTES.md +39 -0
- vmware_storage-1.2.0/config.example.yaml +10 -0
- vmware_storage-1.2.0/docker-compose.yml +8 -0
- vmware_storage-1.2.0/mcp_server/__init__.py +0 -0
- vmware_storage-1.2.0/mcp_server/__main__.py +5 -0
- vmware_storage-1.2.0/mcp_server/server.py +286 -0
- vmware_storage-1.2.0/pyproject.toml +45 -0
- vmware_storage-1.2.0/server.json +21 -0
- vmware_storage-1.2.0/skills/vmware-storage/SKILL.md +86 -0
- vmware_storage-1.2.0/tests/__init__.py +0 -0
- vmware_storage-1.2.0/tests/test_no_destructive_vm_code.py +40 -0
- vmware_storage-1.2.0/vmware_storage/__init__.py +3 -0
- vmware_storage-1.2.0/vmware_storage/cli.py +291 -0
- vmware_storage-1.2.0/vmware_storage/config.py +133 -0
- vmware_storage-1.2.0/vmware_storage/connection.py +99 -0
- vmware_storage-1.2.0/vmware_storage/doctor.py +151 -0
- vmware_storage-1.2.0/vmware_storage/notify/__init__.py +0 -0
- vmware_storage-1.2.0/vmware_storage/notify/audit.py +88 -0
- vmware_storage-1.2.0/vmware_storage/ops/__init__.py +0 -0
- vmware_storage-1.2.0/vmware_storage/ops/datastore_browser.py +219 -0
- vmware_storage-1.2.0/vmware_storage/ops/inventory.py +89 -0
- vmware_storage-1.2.0/vmware_storage/ops/iscsi_config.py +205 -0
- vmware_storage-1.2.0/vmware_storage/ops/vsan.py +148 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Wei Zhou
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vmware-storage
|
|
3
|
+
Version: 1.2.0
|
|
4
|
+
Summary: VMware vSphere storage management: datastores, iSCSI, vSAN. Domain-focused MCP skill.
|
|
5
|
+
Author: Wei Zhou / 周崴
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Requires-Dist: mcp[cli]<2.0,>=1.0
|
|
10
|
+
Requires-Dist: pyaml<27.0,>=24.0
|
|
11
|
+
Requires-Dist: python-dotenv<2.0,>=1.0
|
|
12
|
+
Requires-Dist: pyvmomi<10.0,>=8.0.3.0
|
|
13
|
+
Requires-Dist: rich<15.0,>=13.0
|
|
14
|
+
Requires-Dist: typer<1.0,>=0.12
|
|
15
|
+
Provides-Extra: dev
|
|
16
|
+
Requires-Dist: pytest-cov<8.0,>=5.0; extra == 'dev'
|
|
17
|
+
Requires-Dist: pytest<10.0,>=8.0; extra == 'dev'
|
|
18
|
+
Requires-Dist: ruff<1.0,>=0.5; extra == 'dev'
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
<!-- mcp-name: io.github.zw008/vmware-storage -->
|
|
22
|
+
# VMware Storage
|
|
23
|
+
|
|
24
|
+
[English](README.md) | [中文](README-CN.md)
|
|
25
|
+
|
|
26
|
+
Domain-focused VMware vSphere storage management: datastores, iSCSI, vSAN.
|
|
27
|
+
|
|
28
|
+
> **Part of the VMware MCP Skills family:**
|
|
29
|
+
>
|
|
30
|
+
> | Skill | Scope | Tools |
|
|
31
|
+
> |-------|-------|:-----:|
|
|
32
|
+
> | **vmware-monitor** (read-only) | Inventory, health, alarms, events | 8 |
|
|
33
|
+
> | **vmware-aiops** (full ops) | VM lifecycle, deployment, guest ops, plans | 33 |
|
|
34
|
+
> | **vmware-storage** (this) | Datastores, iSCSI, vSAN | 11 |
|
|
35
|
+
|
|
36
|
+
[](LICENSE)
|
|
37
|
+
|
|
38
|
+
## Quick Install
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# Via PyPI
|
|
42
|
+
uv tool install vmware-storage
|
|
43
|
+
|
|
44
|
+
# Or pip
|
|
45
|
+
pip install vmware-storage
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Configuration
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
mkdir -p ~/.vmware-storage
|
|
52
|
+
cp config.example.yaml ~/.vmware-storage/config.yaml
|
|
53
|
+
# Edit with your vCenter/ESXi credentials
|
|
54
|
+
|
|
55
|
+
echo "VMWARE_MY_VCENTER_PASSWORD=your_password" > ~/.vmware-storage/.env
|
|
56
|
+
chmod 600 ~/.vmware-storage/.env
|
|
57
|
+
|
|
58
|
+
# Verify
|
|
59
|
+
vmware-storage doctor
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## MCP Tools (11)
|
|
63
|
+
|
|
64
|
+
| Category | Tools | Type |
|
|
65
|
+
|----------|-------|------|
|
|
66
|
+
| Datastore | `list_all_datastores`, `browse_datastore`, `scan_datastore_images`, `list_cached_images` | Read |
|
|
67
|
+
| iSCSI | `storage_iscsi_enable`, `storage_iscsi_status`, `storage_iscsi_add_target`, `storage_iscsi_remove_target`, `storage_rescan` | Read/Write |
|
|
68
|
+
| vSAN | `vsan_health`, `vsan_capacity` | Read |
|
|
69
|
+
|
|
70
|
+
## CLI
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# Datastore
|
|
74
|
+
vmware-storage datastore list
|
|
75
|
+
vmware-storage datastore browse datastore01
|
|
76
|
+
vmware-storage datastore scan-images datastore01
|
|
77
|
+
|
|
78
|
+
# iSCSI
|
|
79
|
+
vmware-storage iscsi status esxi-01
|
|
80
|
+
vmware-storage iscsi enable esxi-01
|
|
81
|
+
vmware-storage iscsi add-target esxi-01 192.168.1.100
|
|
82
|
+
vmware-storage iscsi remove-target esxi-01 192.168.1.100
|
|
83
|
+
vmware-storage iscsi rescan esxi-01
|
|
84
|
+
|
|
85
|
+
# vSAN
|
|
86
|
+
vmware-storage vsan health Cluster-Prod
|
|
87
|
+
vmware-storage vsan capacity Cluster-Prod
|
|
88
|
+
|
|
89
|
+
# Diagnostics
|
|
90
|
+
vmware-storage doctor
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## MCP Server
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
# Run directly
|
|
97
|
+
python -m mcp_server
|
|
98
|
+
|
|
99
|
+
# Or via Docker
|
|
100
|
+
docker compose up -d
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Agent Configuration
|
|
104
|
+
|
|
105
|
+
Add to your AI agent's MCP config:
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"mcpServers": {
|
|
110
|
+
"vmware-storage": {
|
|
111
|
+
"command": "vmware-storage-mcp",
|
|
112
|
+
"env": {
|
|
113
|
+
"VMWARE_STORAGE_CONFIG": "~/.vmware-storage/config.yaml"
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Why a Separate Skill?
|
|
121
|
+
|
|
122
|
+
`vmware-aiops` has 33 MCP tools — too heavy for local LLMs (7B-14B). By splitting storage into its own skill:
|
|
123
|
+
|
|
124
|
+
- **11 tools** — fits comfortably in small model context windows
|
|
125
|
+
- **Domain-focused** — storage admins get only what they need
|
|
126
|
+
- **Composable** — use alongside vmware-monitor or vmware-aiops as needed
|
|
127
|
+
|
|
128
|
+
## Version Compatibility
|
|
129
|
+
|
|
130
|
+
| vSphere | Support | Notes |
|
|
131
|
+
|---------|---------|-------|
|
|
132
|
+
| 8.0 | Full | vSAN SDK built into pyVmomi 8.0.3+ |
|
|
133
|
+
| 7.0 | Full | All storage APIs work |
|
|
134
|
+
| 6.7 | Compatible | iSCSI + datastore features work; vSAN limited |
|
|
135
|
+
|
|
136
|
+
## Safety
|
|
137
|
+
|
|
138
|
+
| Feature | Description |
|
|
139
|
+
|---------|-------------|
|
|
140
|
+
| Read-heavy | 6/11 tools are read-only |
|
|
141
|
+
| Input validation | IP addresses and ports validated before iSCSI operations |
|
|
142
|
+
| Audit logging | All operations logged to `~/.vmware-storage/audit.log` |
|
|
143
|
+
| No VM operations | Cannot create, delete, or modify VMs |
|
|
144
|
+
| Credential safety | Passwords only from environment variables, never config files |
|
|
145
|
+
|
|
146
|
+
## License
|
|
147
|
+
|
|
148
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
<!-- mcp-name: io.github.zw008/vmware-storage -->
|
|
2
|
+
# VMware Storage
|
|
3
|
+
|
|
4
|
+
[English](README.md) | [中文](README-CN.md)
|
|
5
|
+
|
|
6
|
+
Domain-focused VMware vSphere storage management: datastores, iSCSI, vSAN.
|
|
7
|
+
|
|
8
|
+
> **Part of the VMware MCP Skills family:**
|
|
9
|
+
>
|
|
10
|
+
> | Skill | Scope | Tools |
|
|
11
|
+
> |-------|-------|:-----:|
|
|
12
|
+
> | **vmware-monitor** (read-only) | Inventory, health, alarms, events | 8 |
|
|
13
|
+
> | **vmware-aiops** (full ops) | VM lifecycle, deployment, guest ops, plans | 33 |
|
|
14
|
+
> | **vmware-storage** (this) | Datastores, iSCSI, vSAN | 11 |
|
|
15
|
+
|
|
16
|
+
[](LICENSE)
|
|
17
|
+
|
|
18
|
+
## Quick Install
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# Via PyPI
|
|
22
|
+
uv tool install vmware-storage
|
|
23
|
+
|
|
24
|
+
# Or pip
|
|
25
|
+
pip install vmware-storage
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Configuration
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
mkdir -p ~/.vmware-storage
|
|
32
|
+
cp config.example.yaml ~/.vmware-storage/config.yaml
|
|
33
|
+
# Edit with your vCenter/ESXi credentials
|
|
34
|
+
|
|
35
|
+
echo "VMWARE_MY_VCENTER_PASSWORD=your_password" > ~/.vmware-storage/.env
|
|
36
|
+
chmod 600 ~/.vmware-storage/.env
|
|
37
|
+
|
|
38
|
+
# Verify
|
|
39
|
+
vmware-storage doctor
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## MCP Tools (11)
|
|
43
|
+
|
|
44
|
+
| Category | Tools | Type |
|
|
45
|
+
|----------|-------|------|
|
|
46
|
+
| Datastore | `list_all_datastores`, `browse_datastore`, `scan_datastore_images`, `list_cached_images` | Read |
|
|
47
|
+
| iSCSI | `storage_iscsi_enable`, `storage_iscsi_status`, `storage_iscsi_add_target`, `storage_iscsi_remove_target`, `storage_rescan` | Read/Write |
|
|
48
|
+
| vSAN | `vsan_health`, `vsan_capacity` | Read |
|
|
49
|
+
|
|
50
|
+
## CLI
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# Datastore
|
|
54
|
+
vmware-storage datastore list
|
|
55
|
+
vmware-storage datastore browse datastore01
|
|
56
|
+
vmware-storage datastore scan-images datastore01
|
|
57
|
+
|
|
58
|
+
# iSCSI
|
|
59
|
+
vmware-storage iscsi status esxi-01
|
|
60
|
+
vmware-storage iscsi enable esxi-01
|
|
61
|
+
vmware-storage iscsi add-target esxi-01 192.168.1.100
|
|
62
|
+
vmware-storage iscsi remove-target esxi-01 192.168.1.100
|
|
63
|
+
vmware-storage iscsi rescan esxi-01
|
|
64
|
+
|
|
65
|
+
# vSAN
|
|
66
|
+
vmware-storage vsan health Cluster-Prod
|
|
67
|
+
vmware-storage vsan capacity Cluster-Prod
|
|
68
|
+
|
|
69
|
+
# Diagnostics
|
|
70
|
+
vmware-storage doctor
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## MCP Server
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# Run directly
|
|
77
|
+
python -m mcp_server
|
|
78
|
+
|
|
79
|
+
# Or via Docker
|
|
80
|
+
docker compose up -d
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Agent Configuration
|
|
84
|
+
|
|
85
|
+
Add to your AI agent's MCP config:
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"mcpServers": {
|
|
90
|
+
"vmware-storage": {
|
|
91
|
+
"command": "vmware-storage-mcp",
|
|
92
|
+
"env": {
|
|
93
|
+
"VMWARE_STORAGE_CONFIG": "~/.vmware-storage/config.yaml"
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Why a Separate Skill?
|
|
101
|
+
|
|
102
|
+
`vmware-aiops` has 33 MCP tools — too heavy for local LLMs (7B-14B). By splitting storage into its own skill:
|
|
103
|
+
|
|
104
|
+
- **11 tools** — fits comfortably in small model context windows
|
|
105
|
+
- **Domain-focused** — storage admins get only what they need
|
|
106
|
+
- **Composable** — use alongside vmware-monitor or vmware-aiops as needed
|
|
107
|
+
|
|
108
|
+
## Version Compatibility
|
|
109
|
+
|
|
110
|
+
| vSphere | Support | Notes |
|
|
111
|
+
|---------|---------|-------|
|
|
112
|
+
| 8.0 | Full | vSAN SDK built into pyVmomi 8.0.3+ |
|
|
113
|
+
| 7.0 | Full | All storage APIs work |
|
|
114
|
+
| 6.7 | Compatible | iSCSI + datastore features work; vSAN limited |
|
|
115
|
+
|
|
116
|
+
## Safety
|
|
117
|
+
|
|
118
|
+
| Feature | Description |
|
|
119
|
+
|---------|-------------|
|
|
120
|
+
| Read-heavy | 6/11 tools are read-only |
|
|
121
|
+
| Input validation | IP addresses and ports validated before iSCSI operations |
|
|
122
|
+
| Audit logging | All operations logged to `~/.vmware-storage/audit.log` |
|
|
123
|
+
| No VM operations | Cannot create, delete, or modify VMs |
|
|
124
|
+
| Credential safety | Passwords only from environment variables, never config files |
|
|
125
|
+
|
|
126
|
+
## License
|
|
127
|
+
|
|
128
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Release Notes
|
|
2
|
+
|
|
3
|
+
## v1.2.0 (2026-03-22)
|
|
4
|
+
|
|
5
|
+
### Initial Release / 首次发布
|
|
6
|
+
|
|
7
|
+
Domain-focused VMware storage skill, split from vmware-aiops for lighter context and better local model compatibility.
|
|
8
|
+
|
|
9
|
+
从 vmware-aiops 中按领域拆分出的存储管理 skill,更轻量,对本地模型更友好。
|
|
10
|
+
|
|
11
|
+
### Datastore Management / 数据存储管理
|
|
12
|
+
|
|
13
|
+
- `list_all_datastores` — List all datastores with capacity, usage %, accessibility
|
|
14
|
+
- `browse_datastore` — Browse files in any datastore directory
|
|
15
|
+
- `scan_datastore_images` — Find OVA, ISO, OVF, VMDK across datastores
|
|
16
|
+
- `list_cached_images` — Query local image registry with filters
|
|
17
|
+
|
|
18
|
+
### iSCSI Configuration / iSCSI 配置
|
|
19
|
+
|
|
20
|
+
- `storage_iscsi_enable` — Enable software iSCSI adapter on ESXi hosts
|
|
21
|
+
- `storage_iscsi_status` — Show adapter status and configured targets
|
|
22
|
+
- `storage_iscsi_add_target` — Add iSCSI send target with auto-rescan
|
|
23
|
+
- `storage_iscsi_remove_target` — Remove target with auto-rescan
|
|
24
|
+
- `storage_rescan` — Force rescan all HBAs and VMFS volumes
|
|
25
|
+
|
|
26
|
+
### vSAN Monitoring / vSAN 监控
|
|
27
|
+
|
|
28
|
+
- `vsan_health` — Cluster health summary with disk group details
|
|
29
|
+
- `vsan_capacity` — Total/used/free capacity with usage percentage
|
|
30
|
+
|
|
31
|
+
### Infrastructure / 基础设施
|
|
32
|
+
|
|
33
|
+
- CLI (`vmware-storage`) with typer — datastore/iscsi/vsan subcommands
|
|
34
|
+
- MCP server (11 tools) via stdio transport
|
|
35
|
+
- Docker one-command launch
|
|
36
|
+
- `vmware-storage doctor` — 6-check environment diagnostics
|
|
37
|
+
- Audit logging (JSON Lines)
|
|
38
|
+
|
|
39
|
+
**PyPI**: `uv tool install vmware-storage==1.2.0`
|
|
File without changes
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
"""MCP server wrapping VMware Storage operations.
|
|
2
|
+
|
|
3
|
+
This module exposes VMware vSphere datastore browsing, iSCSI configuration,
|
|
4
|
+
and vSAN health/capacity tools via the Model Context Protocol (MCP) using
|
|
5
|
+
stdio transport.
|
|
6
|
+
|
|
7
|
+
Tool categories
|
|
8
|
+
---------------
|
|
9
|
+
* **Read-only**: list_all_datastores, browse_datastore, scan_datastore_images,
|
|
10
|
+
list_cached_images, storage_iscsi_status, vsan_health, vsan_capacity
|
|
11
|
+
* **Write**: storage_iscsi_enable, storage_iscsi_add_target,
|
|
12
|
+
storage_iscsi_remove_target, storage_rescan
|
|
13
|
+
|
|
14
|
+
Security considerations
|
|
15
|
+
-----------------------
|
|
16
|
+
* Credentials are loaded from environment variables / .env file.
|
|
17
|
+
* Transport: Uses stdio transport (local only); no network listener.
|
|
18
|
+
* iSCSI operations modify host storage configuration; confirmation recommended.
|
|
19
|
+
|
|
20
|
+
Source: https://github.com/zw008/VMware-Storage
|
|
21
|
+
License: MIT
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import logging
|
|
27
|
+
import os
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
from typing import Any
|
|
30
|
+
|
|
31
|
+
from mcp.server.fastmcp import FastMCP
|
|
32
|
+
|
|
33
|
+
from vmware_storage.config import load_config
|
|
34
|
+
from vmware_storage.connection import ConnectionManager
|
|
35
|
+
from vmware_storage.ops import datastore_browser
|
|
36
|
+
from vmware_storage.ops.inventory import list_datastores
|
|
37
|
+
from vmware_storage.ops.iscsi_config import (
|
|
38
|
+
add_iscsi_target,
|
|
39
|
+
enable_software_iscsi,
|
|
40
|
+
get_iscsi_status,
|
|
41
|
+
remove_iscsi_target,
|
|
42
|
+
rescan_storage,
|
|
43
|
+
)
|
|
44
|
+
from vmware_storage.ops.vsan import get_vsan_capacity, get_vsan_health
|
|
45
|
+
from vmware_storage.notify.audit import AuditLogger
|
|
46
|
+
|
|
47
|
+
logging.basicConfig(level=logging.INFO)
|
|
48
|
+
logger = logging.getLogger("vmware-storage.mcp")
|
|
49
|
+
|
|
50
|
+
mcp = FastMCP("VMware Storage")
|
|
51
|
+
|
|
52
|
+
_audit = AuditLogger()
|
|
53
|
+
|
|
54
|
+
# ---------------------------------------------------------------------------
|
|
55
|
+
# Connection management (lazy-init singleton)
|
|
56
|
+
# ---------------------------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
_conn_mgr: ConnectionManager | None = None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _get_conn_mgr() -> ConnectionManager:
|
|
62
|
+
global _conn_mgr
|
|
63
|
+
if _conn_mgr is None:
|
|
64
|
+
config_path = os.environ.get("VMWARE_STORAGE_CONFIG")
|
|
65
|
+
config = load_config(Path(config_path) if config_path else None)
|
|
66
|
+
_conn_mgr = ConnectionManager(config)
|
|
67
|
+
return _conn_mgr
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _get_connection(target: str | None = None):
|
|
71
|
+
return _get_conn_mgr().connect(target)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# ---------------------------------------------------------------------------
|
|
75
|
+
# Datastore tools
|
|
76
|
+
# ---------------------------------------------------------------------------
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@mcp.tool()
|
|
80
|
+
def list_all_datastores(target: str | None = None) -> list[dict]:
|
|
81
|
+
"""List all datastores with capacity, usage percentage, and accessibility.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
target: Optional vCenter/ESXi target name from config.
|
|
85
|
+
"""
|
|
86
|
+
si = _get_connection(target)
|
|
87
|
+
return list_datastores(si)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@mcp.tool()
|
|
91
|
+
def browse_datastore(
|
|
92
|
+
ds_name: str,
|
|
93
|
+
path: str = "",
|
|
94
|
+
pattern: str = "*",
|
|
95
|
+
target: str | None = None,
|
|
96
|
+
) -> list[dict]:
|
|
97
|
+
"""Browse files in a datastore directory.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
ds_name: Datastore name.
|
|
101
|
+
path: Subdirectory path (empty for root).
|
|
102
|
+
pattern: Glob pattern to filter files (e.g. "*.ova", "*.iso").
|
|
103
|
+
target: Optional vCenter/ESXi target name from config.
|
|
104
|
+
"""
|
|
105
|
+
si = _get_connection(target)
|
|
106
|
+
return datastore_browser.browse_datastore(si, ds_name, path=path, pattern=pattern)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@mcp.tool()
|
|
110
|
+
def scan_datastore_images(
|
|
111
|
+
ds_name: str,
|
|
112
|
+
path: str = "",
|
|
113
|
+
target: str | None = None,
|
|
114
|
+
) -> list[dict]:
|
|
115
|
+
"""Scan a datastore for deployable images (OVA, ISO, OVF, VMDK).
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
ds_name: Datastore name.
|
|
119
|
+
path: Subdirectory path (empty for root).
|
|
120
|
+
target: Optional vCenter/ESXi target name from config.
|
|
121
|
+
"""
|
|
122
|
+
si = _get_connection(target)
|
|
123
|
+
return datastore_browser.scan_images(si, ds_name, path=path)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@mcp.tool()
|
|
127
|
+
def list_cached_images(
|
|
128
|
+
image_type: str | None = None,
|
|
129
|
+
datastore: str | None = None,
|
|
130
|
+
) -> list[dict]:
|
|
131
|
+
"""List images from the local cache registry.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
image_type: Filter by extension (e.g. "ova", "iso").
|
|
135
|
+
datastore: Filter by datastore name.
|
|
136
|
+
"""
|
|
137
|
+
return datastore_browser.list_images(image_type=image_type, datastore=datastore)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
# ---------------------------------------------------------------------------
|
|
141
|
+
# iSCSI tools
|
|
142
|
+
# ---------------------------------------------------------------------------
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@mcp.tool()
|
|
146
|
+
def storage_iscsi_enable(
|
|
147
|
+
host_name: str,
|
|
148
|
+
target: str | None = None,
|
|
149
|
+
) -> str:
|
|
150
|
+
"""Enable the software iSCSI adapter on an ESXi host.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
host_name: ESXi host name.
|
|
154
|
+
target: Optional vCenter/ESXi target name from config.
|
|
155
|
+
"""
|
|
156
|
+
si = _get_connection(target)
|
|
157
|
+
result = enable_software_iscsi(si, host_name)
|
|
158
|
+
_audit.log(target=target or "default", operation="iscsi_enable",
|
|
159
|
+
resource=host_name, parameters={"host_name": host_name}, result=result)
|
|
160
|
+
return result
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
@mcp.tool()
|
|
164
|
+
def storage_iscsi_status(
|
|
165
|
+
host_name: str,
|
|
166
|
+
target: str | None = None,
|
|
167
|
+
) -> dict:
|
|
168
|
+
"""Get iSCSI adapter status and configured send targets.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
host_name: ESXi host name.
|
|
172
|
+
target: Optional vCenter/ESXi target name from config.
|
|
173
|
+
"""
|
|
174
|
+
si = _get_connection(target)
|
|
175
|
+
return get_iscsi_status(si, host_name)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
@mcp.tool()
|
|
179
|
+
def storage_iscsi_add_target(
|
|
180
|
+
host_name: str,
|
|
181
|
+
address: str,
|
|
182
|
+
port: int = 3260,
|
|
183
|
+
target: str | None = None,
|
|
184
|
+
) -> str:
|
|
185
|
+
"""Add an iSCSI send target to an ESXi host and rescan storage.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
host_name: ESXi host name.
|
|
189
|
+
address: iSCSI target IP address.
|
|
190
|
+
port: iSCSI target port (default 3260).
|
|
191
|
+
target: Optional vCenter/ESXi target name from config.
|
|
192
|
+
"""
|
|
193
|
+
si = _get_connection(target)
|
|
194
|
+
result = add_iscsi_target(si, host_name, address, port)
|
|
195
|
+
_audit.log(target=target or "default", operation="iscsi_add_target",
|
|
196
|
+
resource=host_name,
|
|
197
|
+
parameters={"host_name": host_name, "address": address, "port": port},
|
|
198
|
+
result=result)
|
|
199
|
+
return result
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
@mcp.tool()
|
|
203
|
+
def storage_iscsi_remove_target(
|
|
204
|
+
host_name: str,
|
|
205
|
+
address: str,
|
|
206
|
+
port: int = 3260,
|
|
207
|
+
target: str | None = None,
|
|
208
|
+
) -> str:
|
|
209
|
+
"""Remove an iSCSI send target from an ESXi host and rescan storage.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
host_name: ESXi host name.
|
|
213
|
+
address: iSCSI target IP address.
|
|
214
|
+
port: iSCSI target port (default 3260).
|
|
215
|
+
target: Optional vCenter/ESXi target name from config.
|
|
216
|
+
"""
|
|
217
|
+
si = _get_connection(target)
|
|
218
|
+
result = remove_iscsi_target(si, host_name, address, port)
|
|
219
|
+
_audit.log(target=target or "default", operation="iscsi_remove_target",
|
|
220
|
+
resource=host_name,
|
|
221
|
+
parameters={"host_name": host_name, "address": address, "port": port},
|
|
222
|
+
result=result)
|
|
223
|
+
return result
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
@mcp.tool()
|
|
227
|
+
def storage_rescan(
|
|
228
|
+
host_name: str,
|
|
229
|
+
target: str | None = None,
|
|
230
|
+
) -> str:
|
|
231
|
+
"""Rescan all HBAs and VMFS volumes on an ESXi host.
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
host_name: ESXi host name.
|
|
235
|
+
target: Optional vCenter/ESXi target name from config.
|
|
236
|
+
"""
|
|
237
|
+
si = _get_connection(target)
|
|
238
|
+
result = rescan_storage(si, host_name)
|
|
239
|
+
_audit.log(target=target or "default", operation="storage_rescan",
|
|
240
|
+
resource=host_name, parameters={"host_name": host_name}, result=result)
|
|
241
|
+
return result
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
# ---------------------------------------------------------------------------
|
|
245
|
+
# vSAN tools
|
|
246
|
+
# ---------------------------------------------------------------------------
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
@mcp.tool()
|
|
250
|
+
def vsan_health(
|
|
251
|
+
cluster_name: str,
|
|
252
|
+
target: str | None = None,
|
|
253
|
+
) -> dict:
|
|
254
|
+
"""Get vSAN cluster health summary and disk groups.
|
|
255
|
+
|
|
256
|
+
Args:
|
|
257
|
+
cluster_name: Name of the vSAN-enabled cluster.
|
|
258
|
+
target: Optional vCenter/ESXi target name from config.
|
|
259
|
+
"""
|
|
260
|
+
si = _get_connection(target)
|
|
261
|
+
return get_vsan_health(si, cluster_name)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
@mcp.tool()
|
|
265
|
+
def vsan_capacity(
|
|
266
|
+
cluster_name: str,
|
|
267
|
+
target: str | None = None,
|
|
268
|
+
) -> dict:
|
|
269
|
+
"""Get vSAN capacity overview (total/used/free) for a cluster.
|
|
270
|
+
|
|
271
|
+
Args:
|
|
272
|
+
cluster_name: Name of the vSAN-enabled cluster.
|
|
273
|
+
target: Optional vCenter/ESXi target name from config.
|
|
274
|
+
"""
|
|
275
|
+
si = _get_connection(target)
|
|
276
|
+
return get_vsan_capacity(si, cluster_name)
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
# ---------------------------------------------------------------------------
|
|
280
|
+
# Entry point
|
|
281
|
+
# ---------------------------------------------------------------------------
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def main():
|
|
285
|
+
"""Run the MCP server."""
|
|
286
|
+
mcp.run(transport="stdio")
|