vitess.io/vitess@v0.16.2/go/vt/topo/topoproto/tablet.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // Package topoproto contains utility functions to deal with the proto3 18 // structures defined in proto/topodata. 19 package topoproto 20 21 import ( 22 "fmt" 23 "net" 24 "regexp" 25 "sort" 26 "strconv" 27 "strings" 28 29 "google.golang.org/protobuf/proto" 30 31 "k8s.io/apimachinery/pkg/util/sets" 32 33 "vitess.io/vitess/go/netutil" 34 "vitess.io/vitess/go/vt/vterrors" 35 36 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 37 ) 38 39 // This file contains the topodata.Tablet utility functions. 40 41 const ( 42 // VtDbPrefix + keyspace is the default name for databases. 43 VtDbPrefix = "vt_" 44 ) 45 46 // cache the conversion from tablet type enum to lower case string. 47 var tabletTypeLowerName map[int32]string 48 49 func init() { 50 tabletTypeLowerName = make(map[int32]string, len(topodatapb.TabletType_name)) 51 for k, v := range topodatapb.TabletType_name { 52 tabletTypeLowerName[k] = strings.ToLower(v) 53 } 54 } 55 56 // TabletAliasIsZero returns true iff cell and uid are empty 57 func TabletAliasIsZero(ta *topodatapb.TabletAlias) bool { 58 return ta == nil || (ta.Cell == "" && ta.Uid == 0) 59 } 60 61 // TabletAliasEqual returns true if two TabletAlias match 62 func TabletAliasEqual(left, right *topodatapb.TabletAlias) bool { 63 return proto.Equal(left, right) 64 } 65 66 // IsTabletInList returns true if the tablet is in the list of tablets given 67 func IsTabletInList(tablet *topodatapb.Tablet, allTablets []*topodatapb.Tablet) bool { 68 for _, tab := range allTablets { 69 if TabletAliasEqual(tablet.Alias, tab.Alias) { 70 return true 71 } 72 } 73 return false 74 } 75 76 // TabletAliasString formats a TabletAlias 77 func TabletAliasString(ta *topodatapb.TabletAlias) string { 78 if ta == nil { 79 return "<nil>" 80 } 81 return fmt.Sprintf("%v-%010d", ta.Cell, ta.Uid) 82 } 83 84 // TabletAliasUIDStr returns a string version of the uid 85 func TabletAliasUIDStr(ta *topodatapb.TabletAlias) string { 86 return fmt.Sprintf("%010d", ta.Uid) 87 } 88 89 const tabletAliasFormat = "^(?P<cell>[-_.a-zA-Z0-9]+)-(?P<uid>[0-9]+)$" 90 91 var tabletAliasRegexp = regexp.MustCompile(tabletAliasFormat) 92 93 // ParseTabletAlias returns a TabletAlias for the input string, 94 // of the form <cell>-<uid> 95 func ParseTabletAlias(aliasStr string) (*topodatapb.TabletAlias, error) { 96 nameParts := tabletAliasRegexp.FindStringSubmatch(aliasStr) 97 if len(nameParts) != 3 { 98 return nil, fmt.Errorf("invalid tablet alias: '%s', expecting format: '%s'", aliasStr, tabletAliasFormat) 99 } 100 uid, err := ParseUID(nameParts[tabletAliasRegexp.SubexpIndex("uid")]) 101 if err != nil { 102 return nil, vterrors.Wrapf(err, "invalid tablet uid in alias '%s'", aliasStr) 103 } 104 return &topodatapb.TabletAlias{ 105 Cell: nameParts[tabletAliasRegexp.SubexpIndex("cell")], 106 Uid: uid, 107 }, nil 108 } 109 110 // ParseTabletSet returns a set of tablets based on a provided comma separated list of tablets. 111 func ParseTabletSet(tabletListStr string) sets.Set[string] { 112 set := sets.New[string]() 113 if tabletListStr == "" { 114 return set 115 } 116 list := strings.Split(tabletListStr, ",") 117 set.Insert(list...) 118 return set 119 } 120 121 // ParseUID parses just the uid (a number) 122 func ParseUID(value string) (uint32, error) { 123 uid, err := strconv.ParseUint(value, 10, 32) 124 if err != nil { 125 return 0, vterrors.Wrap(err, "bad tablet uid") 126 } 127 return uint32(uid), nil 128 } 129 130 // TabletAliasList is used mainly for sorting 131 type TabletAliasList []*topodatapb.TabletAlias 132 133 // Len is part of sort.Interface 134 func (tal TabletAliasList) Len() int { 135 return len(tal) 136 } 137 138 // Less is part of sort.Interface 139 func (tal TabletAliasList) Less(i, j int) bool { 140 if tal[i].Cell < tal[j].Cell { 141 return true 142 } else if tal[i].Cell > tal[j].Cell { 143 return false 144 } 145 return tal[i].Uid < tal[j].Uid 146 } 147 148 // Swap is part of sort.Interface 149 func (tal TabletAliasList) Swap(i, j int) { 150 tal[i], tal[j] = tal[j], tal[i] 151 } 152 153 // ToStringSlice returns a slice which is the result of mapping 154 // TabletAliasString over a slice of TabletAliases. 155 func (tal TabletAliasList) ToStringSlice() []string { 156 result := make([]string, len(tal)) 157 158 for i, alias := range tal { 159 result[i] = TabletAliasString(alias) 160 } 161 162 return result 163 } 164 165 // AllTabletTypes lists all the possible tablet types 166 var AllTabletTypes = []topodatapb.TabletType{ 167 topodatapb.TabletType_PRIMARY, 168 topodatapb.TabletType_REPLICA, 169 topodatapb.TabletType_RDONLY, 170 topodatapb.TabletType_BATCH, 171 topodatapb.TabletType_SPARE, 172 topodatapb.TabletType_EXPERIMENTAL, 173 topodatapb.TabletType_BACKUP, 174 topodatapb.TabletType_RESTORE, 175 topodatapb.TabletType_DRAINED, 176 } 177 178 // ParseTabletType parses the tablet type into the enum. 179 func ParseTabletType(param string) (topodatapb.TabletType, error) { 180 value, ok := topodatapb.TabletType_value[strings.ToUpper(param)] 181 if !ok { 182 return topodatapb.TabletType_UNKNOWN, fmt.Errorf("unknown TabletType %v", param) 183 } 184 return topodatapb.TabletType(value), nil 185 } 186 187 // ParseTabletTypes parses a comma separated list of tablet types and returns a slice with the respective enums. 188 func ParseTabletTypes(param string) ([]topodatapb.TabletType, error) { 189 var tabletTypes []topodatapb.TabletType 190 for _, typeStr := range strings.Split(param, ",") { 191 t, err := ParseTabletType(typeStr) 192 if err != nil { 193 return nil, err 194 } 195 tabletTypes = append(tabletTypes, t) 196 } 197 return tabletTypes, nil 198 } 199 200 // TabletTypeLString returns a lower case version of the tablet type, 201 // or "unknown" if not known. 202 func TabletTypeLString(tabletType topodatapb.TabletType) string { 203 value, ok := tabletTypeLowerName[int32(tabletType)] 204 if !ok { 205 return "unknown" 206 } 207 return value 208 } 209 210 // IsTypeInList returns true if the given type is in the list. 211 // Use it with AllTabletTypes for instance. 212 func IsTypeInList(tabletType topodatapb.TabletType, types []topodatapb.TabletType) bool { 213 for _, t := range types { 214 if tabletType == t { 215 return true 216 } 217 } 218 return false 219 } 220 221 // MakeStringTypeList returns a list of strings that match the input list. 222 func MakeStringTypeList(types []topodatapb.TabletType) []string { 223 strs := make([]string, len(types)) 224 for i, t := range types { 225 strs[i] = strings.ToLower(t.String()) 226 } 227 sort.Strings(strs) 228 return strs 229 } 230 231 // MysqlAddr returns the host:port of the mysql server. 232 func MysqlAddr(tablet *topodatapb.Tablet) string { 233 return netutil.JoinHostPort(tablet.MysqlHostname, tablet.MysqlPort) 234 } 235 236 // MySQLIP returns the MySQL server's IP by resolving the hostname. 237 func MySQLIP(tablet *topodatapb.Tablet) (string, error) { 238 ipAddrs, err := net.LookupHost(tablet.MysqlHostname) 239 if err != nil { 240 return "", err 241 } 242 return ipAddrs[0], nil 243 } 244 245 // TabletDbName is usually implied by keyspace. Having the shard 246 // information in the database name complicates mysql replication. 247 func TabletDbName(tablet *topodatapb.Tablet) string { 248 if tablet.DbNameOverride != "" { 249 return tablet.DbNameOverride 250 } 251 if tablet.Keyspace == "" { 252 return "" 253 } 254 return VtDbPrefix + tablet.Keyspace 255 } 256 257 // TabletIsAssigned returns if this tablet is assigned to a keyspace and shard. 258 // A "scrap" node will show up as assigned even though its data cannot be used 259 // for serving. 260 func TabletIsAssigned(tablet *topodatapb.Tablet) bool { 261 return tablet != nil && tablet.Keyspace != "" && tablet.Shard != "" 262 } 263 264 // IsServingType returns true if the tablet type is one that should be serving to be healthy, or false if the tablet type 265 // should not be serving in it's healthy state. 266 func IsServingType(tabletType topodatapb.TabletType) bool { 267 switch tabletType { 268 case topodatapb.TabletType_PRIMARY, topodatapb.TabletType_REPLICA, topodatapb.TabletType_BATCH, topodatapb.TabletType_EXPERIMENTAL: 269 return true 270 default: 271 return false 272 } 273 }