This file is indexed.

/usr/share/pyshared/mysql/utilities/common/topology_map.py is in mysql-utilities 1.0.5-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
#
# Copyright (c) 2010, 2012 Oracle and/or its affiliates. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#

"""
This module contains an abstraction of a topolgy map object used to discover
slaves and down-stream replicants for mapping topologies.
"""

import os
from mysql.utilities.exception import UtilError

class TopologyMap(object):
    """The TopologyMap class can be used to connect to a running MySQL server
    and discover its slaves. Setting the option "recurse" permits the
    class to discover a replication topology by finding the slaves for each
    slave for the first master requested.

    To generate a topology map, the caller must call the generate_topology_map()
    method to build the topology. This is left as a separate state because it
    can be a lengthy process thereby too long for a constructor method.
 
    The class also includes methods for printing a graph of the topology
    as well as returning a list of master, slave tuples reporting the
    host name and port for each.
    """

    def __init__(self, seed_server, options={}):
        """Constructor

        seed_server[in]    Master (seed) server connection dictionary                           
        options[in]        options for controlling behavior:
          recurse          If True, check each slave found for add'l slaves
                           Default = False
          prompt_user      If True, prompt user if slave connection fails with
                           master connection parameters
                           Default = False
          quiet            if True, print only the data
                           Default = False
          width            width of report
                           Default = 75
          num_retries      Number of times to retry a failed connection attempt
                           Default = 0
        """
        from mysql.utilities.common.server import get_connection_dictionary
        
        self.recurse = options.get("recurse", False)
        self.quiet = options.get("quiet", False)
        self.prompt_user = options.get("prompt", False)
        self.num_retries = options.get("num_retries", 0)
        self.socket_path = options.get("socket_path", None)
        self.seed_server = seed_server
        self.topology = []


    def _connect(self, conn):
        """Find the attached slaves for a list of server connections.
        
        This method connects to each server in the list and retrieves its slaves.
        It can be called recursively if the recurse parameter is True.
        
        conn[in]           Connection dictionary used to connect to server
    
        Returns tuple - master Server class instance, master:host string
        """
        import getpass
        
        from mysql.utilities.common.server import connect_servers
    
        conn_options = {
            'quiet'     : self.quiet,
            'src_name'  : "master",
            'dest_name' : None,
            'version'   : "5.0.0",
            'unique'    : True,
        }
        
        master_info = "%s:%s" % (conn['host'],
                                 conn['port'])
        master = None
        
        # Clear socket if used with a local server
        if (conn['host'] == 'localhost' or \
            conn['host'] == "127.0.0.1"):
            conn['unix_socket'] = None
        
        # Increment num_retries if not set when --prompt is used
        if self.prompt_user and self.num_retries == 0:
            self.num_retries += 1
    
        # Attempt to connect to the server given the retry limit
        for i in range(0,self.num_retries+1):
            try:
                servers = connect_servers(conn, None, conn_options)
                master = servers[0]
                break
            except UtilError, e:
                print "FAILED.\n"
                if i < self.num_retries and self.prompt_user:
                    print "Connection to %s has failed.\n" % master_info + \
                    "Please enter the following information " + \
                    "to connect to this server."
                    conn['user'] = raw_input("User name: ")
                    conn['passwd'] = getpass.getpass("Password: ")
                else:
                    # retries expired - re-raise error if still failing
                    raise UtilError(e.errmsg)
    
        return (master, master_info)
    
    
    def _check_permissions(self, server, priv):
        """Check to see if user has permissions to execute.
        
        server[in]     Server class instance
        priv[in]       privilege to check
        
        Returns True if permissions available, raises exception if not
        """
        from mysql.utilities.common.user import User

        # Check user permissions 
        user_pass_host = server.user
        if server.passwd is not None and len(server.passwd) > 0:
            user_pass_host += ":" + server.passwd
        user_pass_host += "@" + server.host
        user = User(server, user_pass_host, False)
        if not user.has_privilege("*", "*", priv):
            raise UtilError("Not enough permissions. The user must have the "
                            "%s privilege." % priv)
    

    def _get_slaves(self, max_depth, seed_conn=None, masters_found=[]):
        """Find the attached slaves for a list of server connections.
        
        This method connects to each server in the list and retrieves its slaves.
        It can be called recursively if the recurse option is True.
    
        max_depth[in]       Maximum depth of recursive search
        seed_conn[in]       Current master connection dictionary. Initially,
                            this is the seed server (original master defined
                            in constructor)
        masters_found[in]   a list of all servers in master roles - used to
                            detect a circular replication topology. Initially,
                            this is an empty list as the master detection must
                            occur as the topology is traversed.
    
        Returns list - list of slaves connected to each server in list
        """
        topology = []
        if seed_conn is None:
            seed_conn = self.seed_server
    
        master, master_info = self._connect(seed_conn)
        if master is None:
            return []
   
        # Check user permissions 
        self._check_permissions(master, "REPLICATION SLAVE")

        # Save the master for circular replication identification
        masters_found.append(master_info)
        
        if not self.quiet:
            print "# Finding slaves for master: %s" % master_info
    
        # Get replication topology
        slaves = master.get_slaves()
        slave_list = []
        depth = 0
        if len(slaves) > 0:
            for slave in slaves:
                if slave.find(":") > 0:
                    host, port = slave.split(":")
                else:
                    host = slave
                    port = START_PORT  # Use the default
                slave_conn = self.seed_server.copy()
                slave_conn['host'] = host
                slave_conn['port'] = port
                
                # Now check for circular replication topology - do not recurse
                # if slave is also a master.
                if self.recurse and not slave in masters_found and \
                   ((max_depth is None) or (depth < max_depth)):
                    new_list = self._get_slaves(max_depth, slave_conn,
                                                masters_found)
                    if new_list == []:
                        slave_list.append((slave, []))
                    else:
                        slave_list.append(new_list)
                    depth += 1
                else:
                    slave_list.append((slave, []))
        topology.append((master_info, slave_list))
    
        return topology


    def generate_topology_map(self, max_depth):
        """Find the attached slaves for a list of server connections.
        
        This method generates the topology for the seed server specified at
        instantiation.

        max_depth[in]       Maximum depth of recursive search
        """
        self.topology = self._get_slaves(max_depth)


    def depth(self):
        """Return depth of the topology tree.
        
        Returns int - depth of topology tree.
        """
        return len(self.topology)


    def slaves_found(self):
        """Check to see if any slaves were found.
        
        Returns bool - True if slaves found, False if no slaves.
        """
        return not (len(self.topology) and self.topology[0][1] == [])


    def print_graph(self, topology_list=[], masters_found=[],
                    level=0, preamble=""):
        """Prints a graph of the topology map to standard output.
        
        This method traverses a list of the topology and prints a graph. The
        method is designed to be recursive traversing the list to print the
        slaves for each master in the topology. It will also detect a circular
        replication segment and indicate it on the graph.
        
        topology_list[in]   a list in the form (master, slave) of server
        masters_found[in]   a list of all servers in master roles - used to
                            detect a circular replication topology. Initially,
                            this is an empty list as the master detection must
                            occur as the toplogy is traversed.
        level[in]           the level of indentation - increases with each
                            set of slaves found in topology
        preamble[in]        prefix calculated during recursion to indent text
        """
        # if first iteration, use the topology list generated earlier
        if topology_list == []:
            if self.topology == []:
                # topology not generated yet
                raise UtilError("You must first generate the topology.")
            topology_list = self.topology

        # Detect if we are looking at a sublist or not. Get sublist.
        if len(topology_list) == 1:
            topology_list = topology_list[0]
        master = topology_list[0]
        
        # Save the master for circular replication identification
        masters_found.append(master)

        # For each slave, print the graph link
        slaves = topology_list[1]
        stop = len(slaves)
        if stop > 0:
            # Level 0 is always the first master in the topology.
            if level == 0:
                print "%s (MASTER)" % master
            for i in range(0,stop):
                if len(slaves[i]) == 1:
                    slave = slaves[i][0]
                else:
                    slave = slaves[i]
                new_preamble = preamble + "   "
                print new_preamble+"|"
                role = "(SLAVE"
                if not slave[1] == [] or slave[0] in masters_found:
                    role += " + MASTER"
                role += ")"
                print "%s+--- %s" % (new_preamble, slave[0]),
                
                if (slave[0] in masters_found):
                    print "<-->",
                else:
                    print "-",
                print "%s" % role
                    
                if not slave[1] == []:
                    if i < stop-1:
                        new_preamble += "|"
                    else:
                        new_preamble += " "
                    self.print_graph(slave, masters_found,
                                     level+1, new_preamble)


    def _get_row(self, topology_list):
        """Get a row (master, slave) for the topology map.
        
        topology_list[in]  The topology list
        
        Returns tuple - a row (master, slave)
        """
        new_row = []
        if len(topology_list) == 1:
            topology_list = topology_list[0]
        master = topology_list[0]
        slaves = topology_list[1]
        for slave in slaves:
            if len(slave) == 1:
                new_slave = slave[0]
            else:
                new_slave = slave
            new_row.append((master, new_slave[0]))
            new_row.extend(self._get_row(new_slave))
        return new_row


    def get_topology_map(self):
        """Get a list of the topology map suitable for export
        
        Returns list - a list of masters and their slaves in two columns
        """
        # Get a row for the list
        # make a list from the topology
        master_slaves = [self._get_row(row) for row in self.topology]
        return master_slaves[0]