@@ -141,7 +141,7 @@ async def _list_systems(token: str) -> List[Dict[str, Union[str, int]]]:
141141 return filtered_systems
142142
143143@mcp .tool ()
144- async def get_system_details (system_identifier : Union [ str , int ] , ctx : Context ):
144+ async def get_system_details (system_identifier : str , ctx : Context ):
145145 """Gets details of the specified system.
146146
147147 Args:
@@ -210,18 +210,27 @@ async def get_system_details(system_identifier: Union[str, int], ctx: Context):
210210 await ctx .info (log_string )
211211 return await _get_system_details (system_identifier , ctx .get_state ('token' ))
212212
213- async def _get_system_details (system_identifier : Union [ str , int ] , token : str ) -> Dict [str , Any ]:
213+ async def _get_system_details (system_identifier : str , token : str ) -> Dict [str , Any ]:
214214 system_id = await _resolve_system_id (system_identifier , token )
215215
216216 async with httpx .AsyncClient (verify = CONFIG ["UYUNI_MCP_SSL_VERIFY" ]) as client :
217- details_call : Coroutine = call_uyuni_api (
218- client = client ,
219- method = "GET" ,
220- api_path = "/rhn/manager/api/system/getDetails" ,
221- params = {'sid' : system_id },
222- error_context = f"Fetching details for system { system_id } " ,
223- token = token
224- )
217+ try :
218+ details_result = await call_uyuni_api (
219+ client = client ,
220+ method = "GET" ,
221+ api_path = "/rhn/manager/api/system/getDetails" ,
222+ params = {'sid' : system_id },
223+ error_context = f"Fetching details for system { system_id } " ,
224+ token = token
225+ )
226+ except UnexpectedResponse as e :
227+ logger .warning (f"System { system_id } not found or API error: { e } " )
228+ return {}
229+
230+ if not isinstance (details_result , dict ) or 'id' not in details_result :
231+ logger .warning (f"System { system_id } not found or details could not be fetched. Result: { details_result } " )
232+ return {}
233+
225234 uuid_call : Coroutine = call_uyuni_api (
226235 client = client ,
227236 method = "GET" ,
@@ -256,60 +265,54 @@ async def _get_system_details(system_identifier: Union[str, int], token: str) ->
256265 )
257266
258267 results = await asyncio .gather (
259- details_call ,
260268 uuid_call ,
261269 cpu_call ,
262270 network_call ,
263- products_call
271+ products_call ,
272+ return_exceptions = True
264273 )
265274
266- details_result , uuid_result , cpu_result , network_result , products_result = results
267-
268- if isinstance (details_result , dict ):
269- # Only add the identifier if the API returned actual data
270- system_details = {
271- "system_id" : details_result ["id" ],
272- "system_name" : details_result ["profile_name" ],
273- "last_boot" : details_result ["last_boot" ],
274- "uuid" : uuid_result
275- }
275+ uuid_result , cpu_result , network_result , products_result = results
276276
277- if isinstance (cpu_result , dict ):
278- cpu_details = {
279- "family" : cpu_result ["family" ],
280- "mhz" : cpu_result ["mhz" ],
281- "model" : cpu_result ["model" ],
282- "vendor" : cpu_result ["vendor" ],
283- "arch" : cpu_result ["arch" ]
284- }
285- system_details ["cpu" ] = cpu_details
286- else :
287- logger .error (f"Unexpected API response when getting CPU information for system { system_id } " )
288- logger .error (cpu_result )
289-
290- if isinstance (network_result , dict ):
291- network_details = {
292- "hostname" : network_result ["hostname" ],
293- "ip" : network_result ["ip" ],
294- "ip6" : network_result ["ip6" ]
295- }
296- system_details ["network" ] = network_details
297- else :
298- logger .error (f"Unexpected API response when getting network information for system { system_id } " )
299- logger .error (network_result )
277+ system_details = {
278+ "system_id" : details_result ["id" ],
279+ "system_name" : details_result ["profile_name" ],
280+ "last_boot" : details_result ["last_boot" ],
281+ "uuid" : uuid_result if not isinstance (uuid_result , Exception ) else None
282+ }
300283
301- if isinstance (products_result , list ):
302- base_product = [p ["friendlyName" ] for p in products_result if p ["isBaseProduct" ]]
303- system_details ["installed_products" ] = base_product
304- else :
305- logger .error (f"Unexpected API response when getting installed products for system { system_id } " )
306- logger .error (products_result )
284+ if isinstance (cpu_result , dict ):
285+ cpu_details = {
286+ "family" : cpu_result .get ("family" ),
287+ "mhz" : cpu_result .get ("mhz" ),
288+ "model" : cpu_result .get ("model" ),
289+ "vendor" : cpu_result .get ("vendor" ),
290+ "arch" : cpu_result .get ("arch" )
291+ }
292+ system_details ["cpu" ] = cpu_details
293+ else :
294+ logger .error (f"Unexpected API response when getting CPU information for system { system_id } " )
295+ logger .error (cpu_result )
296+
297+ if isinstance (network_result , dict ):
298+ network_details = {
299+ "hostname" : network_result .get ("hostname" ),
300+ "ip" : network_result .get ("ip" ),
301+ "ip6" : network_result .get ("ip6" )
302+ }
303+ system_details ["network" ] = network_details
304+ else :
305+ logger .error (f"Unexpected API response when getting network information for system { system_id } " )
306+ logger .error (network_result )
307307
308- return system_details
308+ if isinstance (products_result , list ):
309+ base_product = [p ["friendlyName" ] for p in products_result if p .get ("isBaseProduct" )]
310+ system_details ["installed_products" ] = base_product
309311 else :
310- logger .error (f"Unexpected API response when getting details for system { system_id } " )
311- logger .error (details_result )
312- return {}
312+ logger .error (f"Unexpected API response when getting installed products for system { system_id } " )
313+ logger .error (products_result )
314+
315+ return system_details
313316
314317@mcp .tool ()
315318async def get_system_event_history (system_identifier : Union [str , int ], ctx : Context , offset : int = 0 , limit : int = 10 , earliest_date : str = None ):
@@ -1627,6 +1630,26 @@ async def create_system_group(name: str, ctx: Context, description: str = "", co
16271630 logger .info (log_string )
16281631 await ctx .info (log_string )
16291632
1633+ token = ctx .get_state ('token' )
1634+
1635+ # Check if group already exists
1636+ async with httpx .AsyncClient (verify = CONFIG ["UYUNI_MCP_SSL_VERIFY" ]) as client :
1637+ existing_groups = await call_uyuni_api (
1638+ client = client ,
1639+ method = "GET" ,
1640+ api_path = '/rhn/manager/api/systemgroup/listAllGroups' ,
1641+ error_context = "checking existing system groups" ,
1642+ token = token
1643+ )
1644+
1645+ if isinstance (existing_groups , list ):
1646+ for group in existing_groups :
1647+ if isinstance (group , dict ) and group .get ('name' ) == name :
1648+ msg = f"System group '{ name } ' already exists. No action taken."
1649+ logger .info (msg )
1650+ await ctx .info (msg )
1651+ return msg
1652+
16301653 is_confirmed = _to_bool (confirm )
16311654
16321655 if not is_confirmed :
@@ -1641,7 +1664,7 @@ async def create_system_group(name: str, ctx: Context, description: str = "", co
16411664 api_path = create_group_path ,
16421665 json_body = {"name" : name , "description" : description },
16431666 error_context = f"creating system group '{ name } '" ,
1644- token = ctx . get_state ( ' token' )
1667+ token = token
16451668 )
16461669
16471670 if isinstance (api_result , dict ) and 'id' in api_result :
0 commit comments