@@ -74,7 +74,6 @@ def wait_until_mysql_connection(self) -> None:
74
74
import logging
75
75
import os
76
76
import re
77
- import socket
78
77
import sys
79
78
import time
80
79
from abc import ABC , abstractmethod
@@ -134,7 +133,7 @@ def wait_until_mysql_connection(self) -> None:
134
133
# Increment this major API version when introducing breaking changes
135
134
LIBAPI = 0
136
135
137
- LIBPATCH = 87
136
+ LIBPATCH = 88
138
137
139
138
UNIT_TEARDOWN_LOCKNAME = "unit-teardown"
140
139
UNIT_ADD_LOCKNAME = "unit-add"
@@ -495,11 +494,16 @@ def get_unit_hostname(self):
495
494
raise NotImplementedError
496
495
497
496
@abstractmethod
498
- def get_unit_address (self , unit : Unit ) -> str :
497
+ def get_unit_address (self , unit : Unit , relation_name : str ) -> str :
499
498
"""Return unit address."""
500
499
# each platform has its own way to get an arbitrary unit address
501
500
raise NotImplementedError
502
501
502
+ @staticmethod
503
+ def get_unit_label (unit : Unit ) -> str :
504
+ """Return unit label."""
505
+ return unit .name .replace ("/" , "-" )
506
+
503
507
def _on_get_password (self , event : ActionEvent ) -> None :
504
508
"""Action used to retrieve the system user's password."""
505
509
username = event .params .get ("username" ) or ROOT_USERNAME
@@ -633,7 +637,7 @@ def cluster_initialized(self) -> bool:
633
637
for unit in self .app_units :
634
638
try :
635
639
if unit != self .unit and self ._mysql .cluster_metadata_exists (
636
- self .get_unit_address (unit )
640
+ self .get_unit_address (unit , PEER )
637
641
):
638
642
return True
639
643
elif self ._mysql .cluster_metadata_exists ():
@@ -652,13 +656,14 @@ def only_one_cluster_node_thats_uninitialized(self) -> Optional[bool]:
652
656
total_cluster_nodes = 0
653
657
for unit in self .app_units :
654
658
total_cluster_nodes += self ._mysql .get_cluster_node_count (
655
- from_instance = self .get_unit_address (unit )
659
+ from_instance = self .get_unit_address (unit , PEER )
656
660
)
657
661
658
662
total_online_cluster_nodes = 0
659
663
for unit in self .app_units :
660
664
total_online_cluster_nodes += self ._mysql .get_cluster_node_count (
661
- from_instance = self .get_unit_address (unit ), node_status = MySQLMemberState ["ONLINE" ]
665
+ from_instance = self .get_unit_address (unit , PEER ),
666
+ node_status = MySQLMemberState .ONLINE ,
662
667
)
663
668
664
669
return total_cluster_nodes == 1 and total_online_cluster_nodes == 0
@@ -669,15 +674,16 @@ def cluster_fully_initialized(self) -> bool:
669
674
670
675
Fully initialized means that all unit that can be joined are joined.
671
676
"""
672
- return self ._mysql .get_cluster_node_count (node_status = MySQLMemberState [ " ONLINE" ] ) == min (
677
+ return self ._mysql .get_cluster_node_count (node_status = MySQLMemberState . ONLINE ) == min (
673
678
GR_MAX_MEMBERS , self .app .planned_units ()
674
679
)
675
680
676
681
@property
677
682
def unit_configured (self ) -> bool :
678
683
"""Check if the unit is configured to be part of the cluster."""
679
684
return self ._mysql .is_instance_configured_for_innodb (
680
- self .get_unit_address (self .unit ), self .unit_label
685
+ self .get_unit_address (self .unit , PEER ),
686
+ self .unit_label ,
681
687
)
682
688
683
689
@property
@@ -707,7 +713,7 @@ def app_units(self) -> set[Unit]:
707
713
@property
708
714
def unit_label (self ):
709
715
"""Return unit label."""
710
- return self .unit . name . replace ( "/" , "-" )
716
+ return self .get_unit_label ( self . unit )
711
717
712
718
@property
713
719
def _is_peer_data_set (self ) -> bool :
@@ -774,6 +780,37 @@ def peer_relation_data(self, scope: Scopes) -> DataPeerData:
774
780
elif scope == UNIT_SCOPE :
775
781
return self .peer_relation_unit
776
782
783
+ def get_cluster_endpoints (self , relation_name : str ) -> Tuple [str , str , str ]:
784
+ """Return (rw, ro, offline) endpoints tuple names or IPs."""
785
+ repl_topology = self ._mysql .get_cluster_topology ()
786
+ repl_cluster = self ._mysql .is_cluster_replica ()
787
+
788
+ if not repl_topology :
789
+ raise MySQLGetClusterEndpointsError ("Failed to get endpoints from cluster topology" )
790
+
791
+ unit_labels = {self .get_unit_label (unit ): unit for unit in self .app_units }
792
+
793
+ no_endpoints = set ()
794
+ ro_endpoints = set ()
795
+ rw_endpoints = set ()
796
+
797
+ for k , v in repl_topology .items ():
798
+ address = f"{ self .get_unit_address (unit_labels [k ], relation_name )} :3306"
799
+
800
+ if v ["status" ] != MySQLMemberState .ONLINE :
801
+ no_endpoints .add (address )
802
+ if v ["status" ] == MySQLMemberState .ONLINE and v ["mode" ] == "r/o" :
803
+ ro_endpoints .add (address )
804
+ if v ["status" ] == MySQLMemberState .ONLINE and v ["mode" ] == "r/w" and not repl_cluster :
805
+ rw_endpoints .add (address )
806
+
807
+ # Replica return global primary address
808
+ if repl_cluster :
809
+ primary_address = f"{ self ._mysql .get_cluster_set_global_primary_address ()} :3306"
810
+ rw_endpoints .add (primary_address )
811
+
812
+ return "," .join (rw_endpoints ), "," .join (ro_endpoints ), "," .join (no_endpoints )
813
+
777
814
def get_secret (
778
815
self ,
779
816
scope : Scopes ,
@@ -2144,51 +2181,6 @@ def get_cluster_node_count(
2144
2181
2145
2182
return int (matches .group (1 )) if matches else 0
2146
2183
2147
- def get_cluster_endpoints (self , get_ips : bool = True ) -> Tuple [str , str , str ]:
2148
- """Return (rw, ro, ofline) endpoints tuple names or IPs."""
2149
- status = self .get_cluster_status ()
2150
-
2151
- if not status :
2152
- raise MySQLGetClusterEndpointsError ("Failed to get endpoints from cluster status" )
2153
-
2154
- topology = status ["defaultreplicaset" ]["topology" ]
2155
-
2156
- def _get_host_ip (host : str ) -> str :
2157
- try :
2158
- port = None
2159
- if ":" in host :
2160
- host , port = host .split (":" )
2161
-
2162
- host_ip = socket .gethostbyname (host )
2163
- return f"{ host_ip } :{ port } " if port else host_ip
2164
- except socket .gaierror :
2165
- raise MySQLGetClusterEndpointsError (f"Failed to query IP for host { host } " )
2166
-
2167
- ro_endpoints = {
2168
- _get_host_ip (v ["address" ]) if get_ips else v ["address" ]
2169
- for v in topology .values ()
2170
- if v ["mode" ] == "r/o" and v ["status" ] == MySQLMemberState .ONLINE
2171
- }
2172
-
2173
- if self .is_cluster_replica ():
2174
- # replica return global primary address
2175
- global_primary = self .get_cluster_set_global_primary_address ()
2176
- if not global_primary :
2177
- raise MySQLGetClusterEndpointsError ("Failed to get global primary address" )
2178
- rw_endpoints = {_get_host_ip (global_primary ) if get_ips else global_primary }
2179
- else :
2180
- rw_endpoints = {
2181
- _get_host_ip (v ["address" ]) if get_ips else v ["address" ]
2182
- for v in topology .values ()
2183
- if v ["mode" ] == "r/w" and v ["status" ] == MySQLMemberState .ONLINE
2184
- }
2185
- # won't get offline endpoints to IP as they maybe unreachable
2186
- no_endpoints = {
2187
- v ["address" ] for v in topology .values () if v ["status" ] != MySQLMemberState .ONLINE
2188
- }
2189
-
2190
- return "," .join (rw_endpoints ), "," .join (ro_endpoints ), "," .join (no_endpoints )
2191
-
2192
2184
def execute_remove_instance (
2193
2185
self , connect_instance : Optional [str ] = None , force : bool = False
2194
2186
) -> None :
@@ -2478,12 +2470,21 @@ def get_cluster_set_global_primary_address(
2478
2470
2479
2471
return address
2480
2472
2481
- def get_primary_label (self ) -> Optional [str ]:
2482
- """Get the label of the cluster's primary ."""
2473
+ def get_cluster_topology (self ) -> Optional [dict ]:
2474
+ """Get the cluster topology ."""
2483
2475
status = self .get_cluster_status ()
2484
2476
if not status :
2485
2477
return None
2486
- for label , value in status ["defaultreplicaset" ]["topology" ].items ():
2478
+
2479
+ return status ["defaultreplicaset" ]["topology" ]
2480
+
2481
+ def get_primary_label (self ) -> Optional [str ]:
2482
+ """Get the label of the cluster's primary."""
2483
+ topology = self .get_cluster_topology ()
2484
+ if not topology :
2485
+ return None
2486
+
2487
+ for label , value in topology .items ():
2487
2488
if value ["memberrole" ] == "primary" :
2488
2489
return label
2489
2490
0 commit comments