1
2 import posixpath
3 from starcluster.clustersetup import DefaultClusterSetup
4 from starcluster.logger import log
5
6 ndb_mgmd_template = \
7 '''
8 [NDBD DEFAULT]
9 NoOfReplicas=%(num_replicas)s
10 DataMemory=%(data_memory)s # How much memory to allocate for data storage
11 IndexMemory=%(index_memory)s # How much memory to allocate for index storage
12 [MYSQLD DEFAULT]
13 [NDB_MGMD DEFAULT]
14 [TCP DEFAULT]
15 # Section for the cluster management node
16 [NDB_MGMD]
17 # IP address of the management node (this system)
18 HostName=%(mgm_ip)s
19 # Section for the storage nodes
20 '''
21
22 ndb_mgmd_storage = \
23 '''
24 [NDBD]
25 HostName=%(storage_ip)s
26 DataDir=%(data_dir)s
27 BackupDataDir=%(backup_data_dir)s
28 '''
29
30 MY_CNF = \
31 '''
32 #
33 # The MySQL database server configuration file.
34 #
35 # You can copy this to one of:
36 # - "/etc/mysql/my.cnf" to set global options,
37 # - "~/.my.cnf" to set user-specific options.
38 #
39 # One can use all long options that the program supports.
40 # Run program with --help to get a list of available options and with
41 # --log.info-defaults to see which it would actually understand and use.
42 #
43 # For explanations see
44 # http://dev.mysql.com/doc/mysql/en/server-system-variables.html
45
46 # This will be passed to all mysql clients
47 # It has been reported that passwords should be enclosed with ticks/quotes
48 # especially if they contain "#" chars...
49 # Remember to edit /etc/mysql/debian.cnf when changing the socket location.
50 [client]
51 port = 3306
52 socket = /var/run/mysqld/mysqld.sock
53
54 # Here is entries for some specific programs
55 # The following values assume you have at least 32M ram
56
57 # This was formally known as [safe_mysqld]. Both versions are currently parsed.
58 [mysqld_safe]
59 socket = /var/run/mysqld/mysqld.sock
60 nice = 0
61
62 [mysqld]
63 #
64 # * Basic Settings
65 #
66
67 #
68 # * IMPORTANT
69 # If you make changes to these settings and your system uses apparmor, you
70 # may also need to also adjust /etc/apparmor.d/usr.sbin.mysqld.
71 #
72
73 user = mysql
74 socket = /var/run/mysqld/mysqld.sock
75 port = 3306
76 basedir = /usr
77 datadir = /var/lib/mysql
78 tmpdir = /tmp
79 skip-external-locking
80 #
81 # Instead of skip-networking the default is now to listen only on
82 # localhost which is more compatible and is not less secure.
83 bind-address = 127.0.0.1
84 #
85 # * Fine Tuning
86 #
87 key_buffer = 16M
88 max_allowed_packet = 16M
89 thread_stack = 192K
90 thread_cache_size = 8
91 # This replaces the startup script and checks MyISAM tables if needed
92 # the first time they are touched
93 myisam-recover = BACKUP
94 #max_connections = 100
95 #table_cache = 64
96 #thread_concurrency = 10
97 #
98 # * Query Cache Configuration
99 #
100 query_cache_limit = 1M
101 query_cache_size = 16M
102 #
103 # * Logging and Replication
104 #
105 # Both location gets rotated by the cronjob.
106 # Be aware that this log type is a performance killer.
107 # As of 5.1 you can enable the log at runtime!
108 #general_log_file = /var/log/mysql/mysql.log
109 #general_log = 1
110
111 log_error = /var/log/mysql/error.log
112
113 # Here you can see queries with especially long duration
114 #log_slow_queries = /var/log/mysql/mysql-slow.log
115 #long_query_time = 2
116 #log-queries-not-using-indexes
117 #
118 # The following can be used as easy to replay backup logs or for replication.
119 # note: if you are setting up a replication slave, see README.Debian about
120 # other settings you may need to change.
121 #server-id = 1
122 #log_bin = /var/log/mysql/mysql-bin.log
123 expire_logs_days = 10
124 max_binlog_size = 100M
125 #binlog_do_db = include_database_name
126 #binlog_ignore_db = include_database_name
127 #
128 # * InnoDB
129 #
130 # InnoDB is enabled by default with a 10MB datafile in /var/lib/mysql/.
131 # Read the manual for more InnoDB related options. There are many!
132 #
133 # * Security Features
134 #
135 # Read the manual, too, if you want chroot!
136 # chroot = /var/lib/mysql/
137 #
138 # For generating SSL certificates I recommend the OpenSSL GUI "tinyca".
139 #
140 # ssl-ca=/etc/mysql/cacert.pem
141 # ssl-cert=/etc/mysql/server-cert.pem
142 # ssl-key=/etc/mysql/server-key.pem
143
144 # Cluster Configuration
145 ndbcluster
146 # IP address of management node
147 ndb-connectstring=%(mgm_ip)s
148
149 [mysqldump]
150 quick
151 quote-names
152 max_allowed_packet = 16M
153
154 [mysql]
155 #no-auto-rehash # faster start of mysql but no tab completion
156
157 [isamchk]
158 key_buffer = 16M
159
160 [MYSQL_CLUSTER]
161 ndb-connectstring=%(mgm_ip)s
162
163 #
164 # * IMPORTANT: Additional settings that can override those from this file!
165 # The files must end with '.cnf', otherwise they'll be ignored.
166 #
167 !includedir /etc/mysql/conf.d/
168 '''
169
170
172 """
173 This plugin configures a mysql cluster on StarCluster
174 Author: Marc Resnick
175
176 Steps for mysql-cluster to work:
177 1. mkdir -p /var/lib/mysql-cluster/backup
178 2. chown -R mysql:mysql /var/lib/mysql-cluster/
179 3. generate ndb-mgmd for master
180 4. generate my.cnf for data nodes
181 5. /etc/init.d/mysql-ndb-mgm restart on master
182 6. pkill -9 mysql on data nodes
183 7. /etc/init.d/mysql start on data nodes
184 8. /etc/init.d/mysql-ndb restart on data nodes
185
186 Correction to above, do this:
187 1. define plugin section in config named mysql
188 2. start cluster mysql (will fail)
189 3. starcluster runplugin mysql mysql
190 """
191 - def __init__(self, num_replicas, data_memory, index_memory, dump_file,
192 dump_interval, dedicated_query, num_data_nodes):
193 super(MysqlCluster, self).__init__()
194 self._num_replicas = int(num_replicas)
195 self._data_memory = data_memory
196 self._index_memory = index_memory
197 self._dump_file = dump_file
198 self._dump_interval = dump_interval
199 self._dedicated_query = dedicated_query.lower() == 'true'
200 self._num_data_nodes = int(num_data_nodes)
201
203 preseedf = '/tmp/mysql-preseed.txt'
204 mysqlpreseed = node.ssh.remote_file(preseedf, 'w')
205 preseeds = """\
206 mysql-server mysql-server/root_password select
207 mysql-server mysql-server/root_password seen true
208 mysql-server mysql-server/root_password_again select
209 mysql-server mysql-server/root_password_again seen true
210 """
211 mysqlpreseed.write(preseeds)
212 mysqlpreseed.close()
213 node.ssh.execute('debconf-set-selections < %s' % mysqlpreseed.name)
214 node.ssh.execute('rm %s' % mysqlpreseed.name)
215 node.apt_install('mysql-cluster-server')
216
218 nconn = node.ssh
219 nconn.execute('pkill -9 mysql; pkill -9 ndb',
220 ignore_exit_status=True)
221 nconn.execute('mkdir -p /var/lib/mysql-cluster/BACKUP')
222 nconn.execute('chown -R mysql:mysql /var/lib/mysql-cluster')
223
229
230 - def run(self, nodes, master, user, user_shell, volumes):
231 log.info("Installing mysql-cluster-server on all nodes...")
232 for node in nodes:
233 self.pool.simple_job(self._install_mysql_cluster, (node),
234 jobid=node.alias)
235 self.pool.wait(len(nodes))
236 mconn = master.ssh
237 mconn.execute('rm -f /usr/mysql-cluster/*')
238
239 self.mgm_ip = master.private_ip_address
240 if not self._dedicated_query:
241 self.storage_ips = [x.private_ip_address for x in nodes[1:]]
242 self.query_ips = self.storage_ips
243 self.data_nodes = nodes[1:]
244 self.query_nodes = nodes
245 else:
246 self.data_nodes = nodes[1:self._num_data_nodes + 1]
247 self.query_nodes = nodes[self._num_data_nodes + 1:]
248 self.query_nodes.append(master)
249 self.storage_ips = [x.private_ip_address for x in self.data_nodes]
250 self.query_ips = [x.private_ip_address for x in self.query_nodes]
251
252 log.info('Backing up and stopping all mysql processes on all nodes')
253 for node in nodes:
254 self.pool.simple_job(self._backup_and_reset, (node),
255 jobid=node.alias)
256 self.pool.wait(len(nodes))
257
258 log.info('Generating ndb_mgmd.cnf...')
259 ndb_mgmd = mconn.remote_file('/etc/mysql/ndb_mgmd.cnf')
260 ndb_mgmd.write(self.generate_ndb_mgmd())
261 ndb_mgmd.close()
262
263 log.info('Generating my.cnf on all nodes')
264 for node in nodes:
265 self.pool.simple_job(self._write_my_cnf, (node), jobid=node.alias)
266 self.pool.wait(len(nodes))
267
268 log.info('Restarting mysql-ndb-mgm on master node...')
269 mconn.execute('/etc/init.d/mysql-ndb-mgm restart')
270
271 log.info('Restarting mysql-ndb on all data nodes...')
272 for node in self.data_nodes:
273 self.pool.simple_job(node.ssh.execute,
274 ('/etc/init.d/mysql-ndb restart'),
275 jobid=node.alias)
276 self.pool.wait(len(self.data_nodes))
277
278 log.info('Starting mysql on all query nodes')
279 for node in self.query_nodes:
280 self.pool.simple_job(node.ssh.execute,
281 ('/etc/init.d/mysql restart'),
282 dict(ignore_exit_status=True),
283 jobid=node.alias)
284 self.pool.wait(len(self.query_nodes))
285
286 dump_file = self._dump_file
287 dump_dir = '/mnt/mysql-cluster-backup'
288 if posixpath.isabs(self._dump_file):
289 dump_dir, dump_file = posixpath.split(self._dump_file)
290 else:
291 log.warn("%s is not an absolute path, defaulting to %s" %
292 (self._dump_file, posixpath.join(dump_dir, dump_file)))
293 name, ext = posixpath.splitext(dump_file)
294 sc_path = posixpath.join(dump_dir, name + '.sc' + ext)
295 orig_path = posixpath.join(dump_dir, dump_file)
296 if not mconn.isdir(dump_dir):
297 log.info("Directory %s does not exist, creating..." % dump_dir)
298 mconn.makedirs(dump_dir)
299 if mconn.isfile(sc_path):
300 mconn.execute('mysql < %s' % sc_path)
301 elif mconn.isfile(orig_path):
302 mconn.execute('mysql < %s' % orig_path)
303 else:
304 log.info('No dump file found, not importing.')
305 log.info('Adding MySQL dump cronjob to master node')
306 cronjob = self.generate_mysqldump_crontab(sc_path)
307 mconn.remove_lines_from_file('/etc/crontab', '#starcluster-mysql')
308 crontab_file = mconn.remote_file('/etc/crontab', 'a')
309 crontab_file.write(cronjob)
310 crontab_file.close()
311 log.info('Management Node: %s' % master.alias)
312 log.info('Data Nodes: \n%s' % '\n'.join([x.alias for x in
313 self.data_nodes]))
314 log.info('Query Nodes: \n%s' % '\n'.join([x.alias for x in
315 self.query_nodes]))
316
318 ndb_mgmd = ndb_mgmd_template % {'num_replicas': self._num_replicas,
319 'data_memory': self._data_memory,
320 'index_memory': self._index_memory,
321 'mgm_ip': self.mgm_ip}
322 for x in self.storage_ips:
323 ctx = {'storage_ip': x,
324 'data_dir': '/var/lib/mysql-cluster',
325 'backup_data_dir': '/var/lib/mysql-cluster'}
326 ndb_mgmd += ndb_mgmd_storage % ctx
327 ndb_mgmd += '\n'
328 if self._dedicated_query:
329 for x in self.query_nodes:
330 ndb_mgmd += '[MYSQLD]\nHostName=%s\n' % x.private_ip_address
331 else:
332 for x in self.query_nodes:
333 ndb_mgmd += '[MYSQLD]\n'
334 return ndb_mgmd
335
337 return MY_CNF % dict(mgm_ip=self.mgm_ip)
338
340 crontab = (
341 '\n*/%(dump_interval)s * * * * root ' +
342 'mysql --batch --skip-column-names --execute="show databases"' +
343 " | egrep -v '(mysql|information_schema)' | " +
344 "xargs mysqldump --add-drop-table --add-drop-database -Y -B" +
345 '> %(loc)s #starcluster-mysql\n'
346 ) % {'dump_interval': self._dump_interval, 'loc': path}
347 return crontab
348