@@ -1722,14 +1722,16 @@ def _update_status(self):
17221722 version_info = f" (v{ installed_version } )" if installed_version else ""
17231723
17241724 if mode == "web" :
1725- # Open the web port for external access
1725+ # Manage web port (close old, open new)
17261726 web_port = self .config .get ("web-port" , 8080 )
1727- try :
1728- self .unit .open_port ("tcp" , web_port )
1729- logger .info (f"Opened port { web_port } /tcp" )
1730- except Exception as e :
1731- logger .warning (f"Failed to open port { web_port } : { e } " )
1732- self .unit .status = ActiveStatus (f"Web server ready{ version_info } " )
1727+ self ._manage_web_port (web_port )
1728+
1729+ # Get web URL for status message
1730+ web_url = self ._get_web_url ()
1731+ url_info = f" { web_url } " if web_url else ""
1732+ self .unit .status = ActiveStatus (
1733+ f"Web server ready{ version_info } { url_info } "
1734+ )
17331735 elif mode == "worker" :
17341736 gpu_status = self .worker_helper .get_gpu_status_message ()
17351737 _ , discovery_status = self ._check_folder_discovery_status ()
@@ -1739,15 +1741,15 @@ def _update_status(self):
17391741 else :
17401742 gpu_status = self .worker_helper .get_gpu_status_message ()
17411743 _ , discovery_status = self ._check_folder_discovery_status ()
1742- # Open web port when running both
1744+ # Manage web port when running both (close old, open new)
17431745 web_port = self .config .get ("web-port" , 8080 )
1744- try :
1745- self . unit . open_port ( "tcp" , web_port )
1746- logger . info ( f"Opened port { web_port } /tcp" )
1747- except Exception as e :
1748- logger . warning ( f"Failed to open port { web_port } : { e } " )
1746+ self . _manage_web_port ( web_port )
1747+
1748+ # Get web URL for status message
1749+ web_url = self . _get_web_url ()
1750+ url_info = f" { web_url } " if web_url else ""
17491751 self .unit .status = ActiveStatus (
1750- f"Ready{ version_info } { gpu_status } { discovery_status } "
1752+ f"Ready{ version_info } { url_info } { gpu_status } { discovery_status } "
17511753 )
17521754
17531755 except Exception as e :
@@ -1789,6 +1791,65 @@ def _get_installed_concourse_version(self) -> Optional[str]:
17891791 logger .debug (f"Could not detect installed version: { e } " )
17901792 return None
17911793
1794+ def _get_web_url (self ) -> Optional [str ]:
1795+ """Get the web server URL (http://IP:PORT) for status display"""
1796+ try :
1797+ # Get the unit's IP address from network binding
1798+ # Try multiple bindings in order of preference
1799+ binding = None
1800+ for binding_name in ["tsa" , "peers" , "" ]:
1801+ try :
1802+ binding = self .model .get_binding (binding_name )
1803+ if binding and binding .network and binding .network .bind_address :
1804+ break
1805+ except Exception :
1806+ continue
1807+
1808+ if not binding or not binding .network or not binding .network .bind_address :
1809+ logger .debug ("Could not determine unit IP address for web URL" )
1810+ return None
1811+
1812+ unit_ip = str (binding .network .bind_address )
1813+ web_port = self .config .get ("web-port" , 8080 )
1814+
1815+ return f"http://{ unit_ip } :{ web_port } "
1816+ except Exception as e :
1817+ logger .debug (f"Could not construct web URL: { e } " )
1818+ return None
1819+
1820+ def _manage_web_port (self , desired_port : int ):
1821+ """
1822+ Manage web server port - close old ports and open the desired port.
1823+
1824+ This ensures only the configured web port is open, closing any
1825+ previously opened ports to handle configuration changes cleanly.
1826+
1827+ Args:
1828+ desired_port: The port number to open for the web server
1829+ """
1830+ try :
1831+ # Get currently opened TCP ports
1832+ opened_ports = self .unit .opened_ports ()
1833+
1834+ # Find TCP ports that should be closed (all TCP ports except the desired one)
1835+ for port_obj in opened_ports :
1836+ if port_obj .protocol == "tcp" and port_obj .port != desired_port :
1837+ try :
1838+ self .unit .close_port ("tcp" , port_obj .port )
1839+ logger .info (f"Closed old port { port_obj .port } /tcp" )
1840+ except Exception as e :
1841+ logger .warning (f"Failed to close port { port_obj .port } /tcp: { e } " )
1842+
1843+ # Open the desired port
1844+ try :
1845+ self .unit .open_port ("tcp" , desired_port )
1846+ logger .info (f"Opened port { desired_port } /tcp" )
1847+ except Exception as e :
1848+ logger .warning (f"Failed to open port { desired_port } /tcp: { e } " )
1849+
1850+ except Exception as e :
1851+ logger .error (f"Port management failed: { e } " , exc_info = True )
1852+
17921853 def _orchestrate_coordinated_upgrade (self , event , version : str ):
17931854 """Orchestrate coordinated upgrade across multiple units (T045)"""
17941855 try :
0 commit comments