Skip to content

Commit a353556

Browse files
committed
improve(network): integrating subnet update feature (#30)
1 parent 368a939 commit a353556

File tree

2 files changed

+22
-64
lines changed

2 files changed

+22
-64
lines changed

src/openstack_mcp_server/tools/network_tools.py

Lines changed: 18 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
)
1010

1111

12+
_UNSET = object()
13+
14+
1215
class NetworkTools:
1316
"""
1417
A class to encapsulate Network-related tools and utilities.
@@ -51,10 +54,6 @@ def register_tools(self, mcp: FastMCP):
5154
mcp.tool()(self.reassign_floating_ip_to_port)
5255
mcp.tool()(self.create_floating_ips_bulk)
5356
mcp.tool()(self.assign_first_available_floating_ip)
54-
mcp.tool()(self.set_subnet_gateway)
55-
mcp.tool()(self.clear_subnet_gateway)
56-
mcp.tool()(self.set_subnet_dhcp_enabled)
57-
mcp.tool()(self.toggle_subnet_dhcp)
5857

5958
def get_networks(
6059
self,
@@ -319,14 +318,26 @@ def update_subnet(
319318
subnet_id: str,
320319
name: str | None = None,
321320
description: str | None = None,
322-
gateway_ip: str | None = None,
321+
gateway_ip: str | None | object = _UNSET,
323322
is_dhcp_enabled: bool | None = None,
324323
dns_nameservers: list[str] | None = None,
325324
allocation_pools: list[dict] | None = None,
326325
host_routes: list[dict] | None = None,
327326
) -> Subnet:
328327
"""
329-
Update an existing Subnet.
328+
Update subnet attributes atomically. Only provided parameters are changed; omitted
329+
parameters remain untouched.
330+
331+
Typical use-cases:
332+
- Set gateway: pass gateway_ip="10.0.0.1".
333+
- Clear gateway: pass gateway_ip=None.
334+
- Enable/disable DHCP: pass is_dhcp_enabled=True or False.
335+
- Batch updates: change name/description and DNS nameservers together.
336+
337+
Notes:
338+
- OpenStack Neutron supports partial updates. Passing None for gateway_ip clears the gateway.
339+
- To emulate a DHCP toggle, read current state and invert it, then call this method with
340+
is_dhcp_enabled set accordingly.
330341
331342
:param subnet_id: ID of the subnet to update
332343
:param name: New subnet name
@@ -344,7 +355,7 @@ def update_subnet(
344355
update_args["name"] = name
345356
if description is not None:
346357
update_args["description"] = description
347-
if gateway_ip is not None:
358+
if gateway_ip is not _UNSET:
348359
update_args["gateway_ip"] = gateway_ip
349360
if is_dhcp_enabled is not None:
350361
update_args["enable_dhcp"] = is_dhcp_enabled
@@ -371,56 +382,6 @@ def delete_subnet(self, subnet_id: str) -> None:
371382
conn.network.delete_subnet(subnet_id, ignore_missing=False)
372383
return None
373384

374-
def set_subnet_gateway(self, subnet_id: str, gateway_ip: str) -> Subnet:
375-
"""
376-
Set or update a subnet's gateway IP.
377-
378-
:param subnet_id: Subnet ID
379-
:param gateway_ip: Gateway IP to set
380-
:return: Updated Subnet object
381-
"""
382-
conn = get_openstack_conn()
383-
subnet = conn.network.update_subnet(subnet_id, gateway_ip=gateway_ip)
384-
return self._convert_to_subnet_model(subnet)
385-
386-
def clear_subnet_gateway(self, subnet_id: str) -> Subnet:
387-
"""
388-
Clear a subnet's gateway IP (set to `None`).
389-
390-
:param subnet_id: Subnet ID
391-
:return: Updated Subnet object
392-
"""
393-
conn = get_openstack_conn()
394-
subnet = conn.network.update_subnet(subnet_id, gateway_ip=None)
395-
return self._convert_to_subnet_model(subnet)
396-
397-
def set_subnet_dhcp_enabled(self, subnet_id: str, enabled: bool) -> Subnet:
398-
"""
399-
Enable or disable DHCP on a subnet.
400-
401-
:param subnet_id: Subnet ID
402-
:param enabled: DHCP enabled state
403-
:return: Updated Subnet object
404-
"""
405-
conn = get_openstack_conn()
406-
subnet = conn.network.update_subnet(subnet_id, enable_dhcp=enabled)
407-
return self._convert_to_subnet_model(subnet)
408-
409-
def toggle_subnet_dhcp(self, subnet_id: str) -> Subnet:
410-
"""
411-
Toggle DHCP enabled state for a subnet.
412-
413-
:param subnet_id: Subnet ID
414-
:return: Updated Subnet object
415-
"""
416-
conn = get_openstack_conn()
417-
current = conn.network.get_subnet(subnet_id)
418-
subnet = conn.network.update_subnet(
419-
subnet_id,
420-
enable_dhcp=False if current.enable_dhcp else True,
421-
)
422-
return self._convert_to_subnet_model(subnet)
423-
424385
def _convert_to_subnet_model(self, openstack_subnet) -> Subnet:
425386
"""
426387
Convert an OpenStack subnet object to a Subnet pydantic model.

tests/tools/test_network_tools.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,11 +1076,11 @@ def test_set_and_clear_subnet_gateway(
10761076
mock_conn.network.update_subnet.return_value = updated
10771077

10781078
tools = self.get_network_tools()
1079-
res1 = tools.set_subnet_gateway("subnet-1", "10.0.0.254")
1079+
res1 = tools.update_subnet("subnet-1", gateway_ip="10.0.0.254")
10801080
assert res1.gateway_ip == "10.0.0.254"
10811081

10821082
updated.gateway_ip = None
1083-
res2 = tools.clear_subnet_gateway("subnet-1")
1083+
res2 = tools.update_subnet("subnet-1", gateway_ip=None)
10841084
assert res2.gateway_ip is None
10851085

10861086
def test_set_and_toggle_subnet_dhcp(
@@ -1107,14 +1107,11 @@ def test_set_and_toggle_subnet_dhcp(
11071107
mock_conn.network.update_subnet.return_value = updated
11081108

11091109
tools = self.get_network_tools()
1110-
res1 = tools.set_subnet_dhcp_enabled("subnet-1", True)
1110+
res1 = tools.update_subnet("subnet-1", is_dhcp_enabled=True)
11111111
assert res1.is_dhcp_enabled is True
11121112

1113-
current = Mock()
1114-
current.enable_dhcp = True
1115-
mock_conn.network.get_subnet.return_value = current
11161113
updated.enable_dhcp = False
1117-
res2 = tools.toggle_subnet_dhcp("subnet-1")
1114+
res2 = tools.update_subnet("subnet-1", is_dhcp_enabled=False)
11181115
assert res2.is_dhcp_enabled is False
11191116

11201117
def test_get_floating_ips_with_filters_and_unassigned(

0 commit comments

Comments
 (0)