1+ from .response .block_storage import (
2+ Volume ,
3+ VolumeAttachment ,
4+ )
5+ from .base import get_openstack_conn
6+ from fastmcp import FastMCP
7+
8+
9+ class BlockStorageTools :
10+ """
11+ A class to encapsulate Block Storage-related tools and utilities.
12+ """
13+
14+ def register_tools (self , mcp : FastMCP ):
15+ """
16+ Register Block Storage-related tools with the FastMCP instance.
17+ """
18+ mcp .tool ()(self .get_volumes )
19+ mcp .tool ()(self .get_volume_details )
20+ mcp .tool ()(self .create_volume )
21+ mcp .tool ()(self .delete_volume )
22+ mcp .tool ()(self .extend_volume )
23+
24+ def get_volumes (self ) -> list [Volume ]:
25+ """
26+ Get the list of Block Storage volumes.
27+
28+ :return: A list of Volume objects representing the volumes.
29+ """
30+ conn = get_openstack_conn ()
31+
32+ # List the volumes
33+ volume_list = []
34+ for volume in conn .block_storage .volumes ():
35+ attachments = []
36+ for attachment in volume .attachments or []:
37+ attachments .append (
38+ VolumeAttachment (
39+ server_id = attachment .get ("server_id" ),
40+ device = attachment .get ("device" ),
41+ attachment_id = attachment .get ("id" ),
42+ )
43+ )
44+
45+ volume_list .append (
46+ Volume (
47+ id = volume .id ,
48+ name = volume .name ,
49+ status = volume .status ,
50+ size = volume .size ,
51+ volume_type = volume .volume_type ,
52+ availability_zone = volume .availability_zone ,
53+ created_at = str (volume .created_at )
54+ if volume .created_at
55+ else None ,
56+ is_bootable = volume .is_bootable ,
57+ is_encrypted = volume .is_encrypted ,
58+ description = volume .description ,
59+ attachments = attachments ,
60+ )
61+ )
62+
63+ return volume_list
64+
65+ def get_volume_details (self , volume_id : str ) -> Volume :
66+ """
67+ Get detailed information about a specific volume.
68+
69+ :param volume_id: The ID of the volume to get details for
70+ :return: A Volume object with detailed information
71+ """
72+ conn = get_openstack_conn ()
73+
74+ volume = conn .block_storage .get_volume (volume_id )
75+
76+ attachments = []
77+ for attachment in volume .attachments or []:
78+ attachments .append (
79+ VolumeAttachment (
80+ server_id = attachment .get ("server_id" ),
81+ device = attachment .get ("device" ),
82+ attachment_id = attachment .get ("id" ),
83+ )
84+ )
85+
86+ return Volume (
87+ id = volume .id ,
88+ name = volume .name ,
89+ status = volume .status ,
90+ size = volume .size ,
91+ volume_type = volume .volume_type ,
92+ availability_zone = volume .availability_zone ,
93+ created_at = str (volume .created_at ),
94+ is_bootable = volume .is_bootable ,
95+ is_encrypted = volume .is_encrypted ,
96+ description = volume .description ,
97+ attachments = attachments ,
98+ )
99+
100+ def create_volume (
101+ self ,
102+ name : str ,
103+ size : int ,
104+ description : str | None = None ,
105+ volume_type : str | None = None ,
106+ availability_zone : str | None = None ,
107+ bootable : bool | None = None ,
108+ image : str | None = None ,
109+ ) -> Volume :
110+ """
111+ Create a new volume.
112+
113+ :param name: Name for the new volume
114+ :param size: Size of the volume in GB
115+ :param description: Optional description for the volume
116+ :param volume_type: Optional volume type
117+ :param availability_zone: Optional availability zone
118+ :param bootable: Optional flag to make the volume bootable
119+ :param image: Optional Image name, ID or object from which to create
120+ :return: The created Volume object
121+ """
122+ conn = get_openstack_conn ()
123+
124+ volume_kwargs = {
125+ "name" : name ,
126+ }
127+
128+ if description is not None :
129+ volume_kwargs ["description" ] = description
130+ if volume_type is not None :
131+ volume_kwargs ["volume_type" ] = volume_type
132+ if availability_zone is not None :
133+ volume_kwargs ["availability_zone" ] = availability_zone
134+
135+ volume = conn .block_storage .create_volume (
136+ size = size , image = image , bootable = bootable , ** volume_kwargs
137+ )
138+
139+ volume_obj = Volume (
140+ id = volume .id ,
141+ name = volume .name ,
142+ status = volume .status ,
143+ size = volume .size ,
144+ volume_type = volume .volume_type ,
145+ availability_zone = volume .availability_zone ,
146+ created_at = str (volume .created_at ),
147+ is_bootable = volume .is_bootable ,
148+ is_encrypted = volume .is_encrypted ,
149+ description = volume .description ,
150+ attachments = [],
151+ )
152+
153+ return volume_obj
154+
155+ def delete_volume (self , volume_id : str , force : bool = False ) -> None :
156+ """
157+ Delete a volume.
158+
159+ :param volume_id: The ID of the volume to delete
160+ :param force: Whether to force delete the volume
161+ :return: None
162+ """
163+ conn = get_openstack_conn ()
164+
165+ conn .block_storage .delete_volume (volume_id , force = force , ignore_missing = False )
166+
167+ def extend_volume (self , volume_id : str , new_size : int ) -> None :
168+ """
169+ Extend a volume to a new size.
170+
171+ :param volume_id: The ID of the volume to extend
172+ :param new_size: The new size in GB (must be larger than current size)
173+ :return: None
174+ """
175+ conn = get_openstack_conn ()
176+
177+ conn .block_storage .extend_volume (volume_id , new_size )
0 commit comments