##// END OF EJS Templates
merge with beta
marcink -
r2780:dd222038 merge default
parent child Browse files
Show More
@@ -1,583 +1,609 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.db_manage
4 4 ~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Database creation, and setup module for RhodeCode. Used for creation
7 7 of database as well as for migration operations
8 8
9 9 :created_on: Apr 10, 2010
10 10 :author: marcink
11 11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 12 :license: GPLv3, see COPYING for more details.
13 13 """
14 14 # This program is free software: you can redistribute it and/or modify
15 15 # it under the terms of the GNU General Public License as published by
16 16 # the Free Software Foundation, either version 3 of the License, or
17 17 # (at your option) any later version.
18 18 #
19 19 # This program is distributed in the hope that it will be useful,
20 20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 22 # GNU General Public License for more details.
23 23 #
24 24 # You should have received a copy of the GNU General Public License
25 25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 26
27 27 import os
28 28 import sys
29 29 import uuid
30 30 import logging
31 31 from os.path import dirname as dn, join as jn
32 32
33 33 from rhodecode import __dbversion__
34 34
35 35 from rhodecode.model.user import UserModel
36 36 from rhodecode.lib.utils import ask_ok
37 37 from rhodecode.model import init_model
38 38 from rhodecode.model.db import User, Permission, RhodeCodeUi, \
39 39 RhodeCodeSetting, UserToPerm, DbMigrateVersion, RepoGroup, \
40 40 UserRepoGroupToPerm
41 41
42 42 from sqlalchemy.engine import create_engine
43 from sqlalchemy.schema import MetaData
44 43 from rhodecode.model.repos_group import ReposGroupModel
45 44 #from rhodecode.model import meta
46 45 from rhodecode.model.meta import Session, Base
47 46
48 47
49 48 log = logging.getLogger(__name__)
50 49
51 50
51 def notify(msg):
52 """
53 Notification for migrations messages
54 """
55 ml = len(msg) + (4 * 2)
56 print >> sys.stdout, ('*** %s ***\n%s' % (msg, '*' * ml)).upper()
57
58
52 59 class DbManage(object):
53 60 def __init__(self, log_sql, dbconf, root, tests=False):
54 61 self.dbname = dbconf.split('/')[-1]
55 62 self.tests = tests
56 63 self.root = root
57 64 self.dburi = dbconf
58 65 self.log_sql = log_sql
59 66 self.db_exists = False
60 67 self.init_db()
61 68
62 69 def init_db(self):
63 70 engine = create_engine(self.dburi, echo=self.log_sql)
64 71 init_model(engine)
65 72 self.sa = Session()
66 73
67 74 def create_tables(self, override=False, defaults={}):
68 75 """
69 76 Create a auth database
70 77 """
71 78 quiet = defaults.get('quiet')
72 79 log.info("Any existing database is going to be destroyed")
73 80 if self.tests or quiet:
74 81 destroy = True
75 82 else:
76 83 destroy = ask_ok('Are you sure to destroy old database ? [y/n]')
77 84 if not destroy:
78 85 sys.exit()
79 86 if destroy:
80 87 Base.metadata.drop_all()
81 88
82 89 checkfirst = not override
83 90 Base.metadata.create_all(checkfirst=checkfirst)
84 91 log.info('Created tables for %s' % self.dbname)
85 92
86 93 def set_db_version(self):
87 94 ver = DbMigrateVersion()
88 95 ver.version = __dbversion__
89 96 ver.repository_id = 'rhodecode_db_migrations'
90 97 ver.repository_path = 'versions'
91 98 self.sa.add(ver)
92 99 log.info('db version set to: %s' % __dbversion__)
93 100
94 101 def upgrade(self):
95 102 """
96 103 Upgrades given database schema to given revision following
97 104 all needed steps, to perform the upgrade
98 105
99 106 """
100 107
101 108 from rhodecode.lib.dbmigrate.migrate.versioning import api
102 109 from rhodecode.lib.dbmigrate.migrate.exceptions import \
103 110 DatabaseNotControlledError
104 111
105 112 if 'sqlite' in self.dburi:
106 113 print (
107 114 '********************** WARNING **********************\n'
108 115 'Make sure your version of sqlite is at least 3.7.X. \n'
109 116 'Earlier versions are known to fail on some migrations\n'
110 117 '*****************************************************\n'
111 118 )
112 119 upgrade = ask_ok('You are about to perform database upgrade, make '
113 120 'sure You backed up your database before. '
114 121 'Continue ? [y/n]')
115 122 if not upgrade:
116 123 sys.exit('Nothing done')
117 124
118 125 repository_path = jn(dn(dn(dn(os.path.realpath(__file__)))),
119 126 'rhodecode/lib/dbmigrate')
120 127 db_uri = self.dburi
121 128
122 129 try:
123 130 curr_version = api.db_version(db_uri, repository_path)
124 131 msg = ('Found current database under version'
125 132 ' control with version %s' % curr_version)
126 133
127 134 except (RuntimeError, DatabaseNotControlledError):
128 135 curr_version = 1
129 136 msg = ('Current database is not under version control. Setting'
130 137 ' as version %s' % curr_version)
131 138 api.version_control(db_uri, repository_path, curr_version)
132 139
133 print (msg)
140 notify(msg)
134 141
135 142 if curr_version == __dbversion__:
136 143 sys.exit('This database is already at the newest version')
137 144
138 145 #======================================================================
139 146 # UPGRADE STEPS
140 147 #======================================================================
148
141 149 class UpgradeSteps(object):
142 150 """
143 151 Those steps follow schema versions so for example schema
144 152 for example schema with seq 002 == step_2 and so on.
145 153 """
146 154
147 155 def __init__(self, klass):
148 156 self.klass = klass
149 157
150 158 def step_0(self):
151 159 # step 0 is the schema upgrade, and than follow proper upgrades
152 print ('attempting to do database upgrade to version %s' \
160 notify('attempting to do database upgrade to version %s' \
153 161 % __dbversion__)
154 162 api.upgrade(db_uri, repository_path, __dbversion__)
155 print ('Schema upgrade completed')
163 notify('Schema upgrade completed')
156 164
157 165 def step_1(self):
158 166 pass
159 167
160 168 def step_2(self):
161 print ('Patching repo paths for newer version of RhodeCode')
169 notify('Patching repo paths for newer version of RhodeCode')
162 170 self.klass.fix_repo_paths()
163 171
164 print ('Patching default user of RhodeCode')
172 notify('Patching default user of RhodeCode')
165 173 self.klass.fix_default_user()
166 174
167 175 log.info('Changing ui settings')
168 176 self.klass.create_ui_settings()
169 177
170 178 def step_3(self):
171 print ('Adding additional settings into RhodeCode db')
179 notify('Adding additional settings into RhodeCode db')
172 180 self.klass.fix_settings()
173 print ('Adding ldap defaults')
181 notify('Adding ldap defaults')
174 182 self.klass.create_ldap_options(skip_existing=True)
175 183
176 184 def step_4(self):
177 print ('create permissions and fix groups')
185 notify('create permissions and fix groups')
178 186 self.klass.create_permissions()
179 187 self.klass.fixup_groups()
180 188
181 189 def step_5(self):
182 190 pass
183 191
184 192 def step_6(self):
185 print ('re-checking permissions')
193
194 notify('re-checking permissions')
186 195 self.klass.create_permissions()
187 196
188 print ('installing new hooks')
197 notify('fixing old PULL hook')
198 _pull = RhodeCodeUi.get_by_key('preoutgoing.pull_logger')
199 if _pull:
200 _pull.ui_key = RhodeCodeUi.HOOK_PULL
201 Session().add(_pull)
202
203 notify('fixing old PUSH hook')
204 _push = RhodeCodeUi.get_by_key('pretxnchangegroup.push_logger')
205 if _push:
206 _push.ui_key = RhodeCodeUi.HOOK_PUSH
207 Session().add(_push)
208
209 notify('installing new pre-push hook')
189 210 hooks4 = RhodeCodeUi()
190 211 hooks4.ui_section = 'hooks'
191 212 hooks4.ui_key = RhodeCodeUi.HOOK_PRE_PUSH
192 213 hooks4.ui_value = 'python:rhodecode.lib.hooks.pre_push'
193 214 Session().add(hooks4)
194 215
216 notify('installing new pre-pull hook')
195 217 hooks6 = RhodeCodeUi()
196 218 hooks6.ui_section = 'hooks'
197 219 hooks6.ui_key = RhodeCodeUi.HOOK_PRE_PULL
198 220 hooks6.ui_value = 'python:rhodecode.lib.hooks.pre_pull'
199 221 Session().add(hooks6)
200 222
201 print ('installing hgsubversion option')
223 notify('installing hgsubversion option')
202 224 # enable hgsubversion disabled by default
203 225 hgsubversion = RhodeCodeUi()
204 226 hgsubversion.ui_section = 'extensions'
205 227 hgsubversion.ui_key = 'hgsubversion'
206 228 hgsubversion.ui_value = ''
207 229 hgsubversion.ui_active = False
208 230 Session().add(hgsubversion)
209 231
210 print ('installing hg git option')
232 notify('installing hg git option')
211 233 # enable hggit disabled by default
212 234 hggit = RhodeCodeUi()
213 235 hggit.ui_section = 'extensions'
214 236 hggit.ui_key = 'hggit'
215 237 hggit.ui_value = ''
216 238 hggit.ui_active = False
217 239 Session().add(hggit)
218 240
219 print ('re-check default permissions')
241 notify('re-check default permissions')
220 242 self.klass.populate_default_permissions()
221 243
222 244 upgrade_steps = [0] + range(curr_version + 1, __dbversion__ + 1)
223 245
224 246 # CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
247 _step = None
225 248 for step in upgrade_steps:
226 print ('performing upgrade step %s' % step)
249 notify('performing upgrade step %s' % step)
227 250 getattr(UpgradeSteps(self), 'step_%s' % step)()
228 251 self.sa.commit()
252 _step = step
253
254 notify('upgrade to version %s successful' % _step)
229 255
230 256 def fix_repo_paths(self):
231 257 """
232 258 Fixes a old rhodecode version path into new one without a '*'
233 259 """
234 260
235 261 paths = self.sa.query(RhodeCodeUi)\
236 262 .filter(RhodeCodeUi.ui_key == '/')\
237 263 .scalar()
238 264
239 265 paths.ui_value = paths.ui_value.replace('*', '')
240 266
241 267 try:
242 268 self.sa.add(paths)
243 269 self.sa.commit()
244 270 except:
245 271 self.sa.rollback()
246 272 raise
247 273
248 274 def fix_default_user(self):
249 275 """
250 276 Fixes a old default user with some 'nicer' default values,
251 277 used mostly for anonymous access
252 278 """
253 279 def_user = self.sa.query(User)\
254 280 .filter(User.username == 'default')\
255 281 .one()
256 282
257 283 def_user.name = 'Anonymous'
258 284 def_user.lastname = 'User'
259 285 def_user.email = 'anonymous@rhodecode.org'
260 286
261 287 try:
262 288 self.sa.add(def_user)
263 289 self.sa.commit()
264 290 except:
265 291 self.sa.rollback()
266 292 raise
267 293
268 294 def fix_settings(self):
269 295 """
270 296 Fixes rhodecode settings adds ga_code key for google analytics
271 297 """
272 298
273 299 hgsettings3 = RhodeCodeSetting('ga_code', '')
274 300
275 301 try:
276 302 self.sa.add(hgsettings3)
277 303 self.sa.commit()
278 304 except:
279 305 self.sa.rollback()
280 306 raise
281 307
282 308 def admin_prompt(self, second=False, defaults={}):
283 309 if not self.tests:
284 310 import getpass
285 311
286 312 # defaults
287 313 username = defaults.get('username')
288 314 password = defaults.get('password')
289 315 email = defaults.get('email')
290 316
291 317 def get_password():
292 318 password = getpass.getpass('Specify admin password '
293 319 '(min 6 chars):')
294 320 confirm = getpass.getpass('Confirm password:')
295 321
296 322 if password != confirm:
297 323 log.error('passwords mismatch')
298 324 return False
299 325 if len(password) < 6:
300 326 log.error('password is to short use at least 6 characters')
301 327 return False
302 328
303 329 return password
304 330 if username is None:
305 331 username = raw_input('Specify admin username:')
306 332 if password is None:
307 333 password = get_password()
308 334 if not password:
309 335 #second try
310 336 password = get_password()
311 337 if not password:
312 338 sys.exit()
313 339 if email is None:
314 340 email = raw_input('Specify admin email:')
315 341 self.create_user(username, password, email, True)
316 342 else:
317 343 log.info('creating admin and regular test users')
318 344 from rhodecode.tests import TEST_USER_ADMIN_LOGIN, \
319 345 TEST_USER_ADMIN_PASS, TEST_USER_ADMIN_EMAIL, \
320 346 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, \
321 347 TEST_USER_REGULAR_EMAIL, TEST_USER_REGULAR2_LOGIN, \
322 348 TEST_USER_REGULAR2_PASS, TEST_USER_REGULAR2_EMAIL
323 349
324 350 self.create_user(TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS,
325 351 TEST_USER_ADMIN_EMAIL, True)
326 352
327 353 self.create_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
328 354 TEST_USER_REGULAR_EMAIL, False)
329 355
330 356 self.create_user(TEST_USER_REGULAR2_LOGIN, TEST_USER_REGULAR2_PASS,
331 357 TEST_USER_REGULAR2_EMAIL, False)
332 358
333 359 def create_ui_settings(self):
334 360 """
335 361 Creates ui settings, fills out hooks
336 362 and disables dotencode
337 363 """
338 364
339 365 #HOOKS
340 366 hooks1_key = RhodeCodeUi.HOOK_UPDATE
341 367 hooks1_ = self.sa.query(RhodeCodeUi)\
342 368 .filter(RhodeCodeUi.ui_key == hooks1_key).scalar()
343 369
344 370 hooks1 = RhodeCodeUi() if hooks1_ is None else hooks1_
345 371 hooks1.ui_section = 'hooks'
346 372 hooks1.ui_key = hooks1_key
347 373 hooks1.ui_value = 'hg update >&2'
348 374 hooks1.ui_active = False
349 375 self.sa.add(hooks1)
350 376
351 377 hooks2_key = RhodeCodeUi.HOOK_REPO_SIZE
352 378 hooks2_ = self.sa.query(RhodeCodeUi)\
353 379 .filter(RhodeCodeUi.ui_key == hooks2_key).scalar()
354 380 hooks2 = RhodeCodeUi() if hooks2_ is None else hooks2_
355 381 hooks2.ui_section = 'hooks'
356 382 hooks2.ui_key = hooks2_key
357 383 hooks2.ui_value = 'python:rhodecode.lib.hooks.repo_size'
358 384 self.sa.add(hooks2)
359 385
360 386 hooks3 = RhodeCodeUi()
361 387 hooks3.ui_section = 'hooks'
362 388 hooks3.ui_key = RhodeCodeUi.HOOK_PUSH
363 389 hooks3.ui_value = 'python:rhodecode.lib.hooks.log_push_action'
364 390 self.sa.add(hooks3)
365 391
366 392 hooks4 = RhodeCodeUi()
367 393 hooks4.ui_section = 'hooks'
368 394 hooks4.ui_key = RhodeCodeUi.HOOK_PRE_PUSH
369 395 hooks4.ui_value = 'python:rhodecode.lib.hooks.pre_push'
370 396 self.sa.add(hooks4)
371 397
372 398 hooks5 = RhodeCodeUi()
373 399 hooks5.ui_section = 'hooks'
374 400 hooks5.ui_key = RhodeCodeUi.HOOK_PULL
375 401 hooks5.ui_value = 'python:rhodecode.lib.hooks.log_pull_action'
376 402 self.sa.add(hooks5)
377 403
378 404 hooks6 = RhodeCodeUi()
379 405 hooks6.ui_section = 'hooks'
380 406 hooks6.ui_key = RhodeCodeUi.HOOK_PRE_PULL
381 407 hooks6.ui_value = 'python:rhodecode.lib.hooks.pre_pull'
382 408 self.sa.add(hooks6)
383 409
384 410 # enable largefiles
385 411 largefiles = RhodeCodeUi()
386 412 largefiles.ui_section = 'extensions'
387 413 largefiles.ui_key = 'largefiles'
388 414 largefiles.ui_value = ''
389 415 self.sa.add(largefiles)
390 416
391 417 # enable hgsubversion disabled by default
392 418 hgsubversion = RhodeCodeUi()
393 419 hgsubversion.ui_section = 'extensions'
394 420 hgsubversion.ui_key = 'hgsubversion'
395 421 hgsubversion.ui_value = ''
396 422 hgsubversion.ui_active = False
397 423 self.sa.add(hgsubversion)
398 424
399 425 # enable hggit disabled by default
400 426 hggit = RhodeCodeUi()
401 427 hggit.ui_section = 'extensions'
402 428 hggit.ui_key = 'hggit'
403 429 hggit.ui_value = ''
404 430 hggit.ui_active = False
405 431 self.sa.add(hggit)
406 432
407 433 def create_ldap_options(self, skip_existing=False):
408 434 """Creates ldap settings"""
409 435
410 436 for k, v in [('ldap_active', 'false'), ('ldap_host', ''),
411 437 ('ldap_port', '389'), ('ldap_tls_kind', 'PLAIN'),
412 438 ('ldap_tls_reqcert', ''), ('ldap_dn_user', ''),
413 439 ('ldap_dn_pass', ''), ('ldap_base_dn', ''),
414 440 ('ldap_filter', ''), ('ldap_search_scope', ''),
415 441 ('ldap_attr_login', ''), ('ldap_attr_firstname', ''),
416 442 ('ldap_attr_lastname', ''), ('ldap_attr_email', '')]:
417 443
418 444 if skip_existing and RhodeCodeSetting.get_by_name(k) != None:
419 445 log.debug('Skipping option %s' % k)
420 446 continue
421 447 setting = RhodeCodeSetting(k, v)
422 448 self.sa.add(setting)
423 449
424 450 def fixup_groups(self):
425 451 def_usr = User.get_by_username('default')
426 452 for g in RepoGroup.query().all():
427 453 g.group_name = g.get_new_name(g.name)
428 454 self.sa.add(g)
429 455 # get default perm
430 456 default = UserRepoGroupToPerm.query()\
431 457 .filter(UserRepoGroupToPerm.group == g)\
432 458 .filter(UserRepoGroupToPerm.user == def_usr)\
433 459 .scalar()
434 460
435 461 if default is None:
436 462 log.debug('missing default permission for group %s adding' % g)
437 463 ReposGroupModel()._create_default_perms(g)
438 464
439 465 def config_prompt(self, test_repo_path='', retries=3, defaults={}):
440 466 _path = defaults.get('repos_location')
441 467 if retries == 3:
442 468 log.info('Setting up repositories config')
443 469
444 470 if _path is not None:
445 471 path = _path
446 472 elif not self.tests and not test_repo_path:
447 473 path = raw_input(
448 474 'Enter a valid absolute path to store repositories. '
449 475 'All repositories in that path will be added automatically:'
450 476 )
451 477 else:
452 478 path = test_repo_path
453 479 path_ok = True
454 480
455 481 # check proper dir
456 482 if not os.path.isdir(path):
457 483 path_ok = False
458 484 log.error('Given path %s is not a valid directory' % path)
459 485
460 486 elif not os.path.isabs(path):
461 487 path_ok = False
462 488 log.error('Given path %s is not an absolute path' % path)
463 489
464 490 # check write access
465 491 elif not os.access(path, os.W_OK) and path_ok:
466 492 path_ok = False
467 493 log.error('No write permission to given path %s' % path)
468 494
469 495 if retries == 0:
470 496 sys.exit('max retries reached')
471 497 if path_ok is False:
472 498 retries -= 1
473 499 return self.config_prompt(test_repo_path, retries)
474 500
475 501 return path
476 502
477 503 def create_settings(self, path):
478 504
479 505 self.create_ui_settings()
480 506
481 507 #HG UI OPTIONS
482 508 web1 = RhodeCodeUi()
483 509 web1.ui_section = 'web'
484 510 web1.ui_key = 'push_ssl'
485 511 web1.ui_value = 'false'
486 512
487 513 web2 = RhodeCodeUi()
488 514 web2.ui_section = 'web'
489 515 web2.ui_key = 'allow_archive'
490 516 web2.ui_value = 'gz zip bz2'
491 517
492 518 web3 = RhodeCodeUi()
493 519 web3.ui_section = 'web'
494 520 web3.ui_key = 'allow_push'
495 521 web3.ui_value = '*'
496 522
497 523 web4 = RhodeCodeUi()
498 524 web4.ui_section = 'web'
499 525 web4.ui_key = 'baseurl'
500 526 web4.ui_value = '/'
501 527
502 528 paths = RhodeCodeUi()
503 529 paths.ui_section = 'paths'
504 530 paths.ui_key = '/'
505 531 paths.ui_value = path
506 532
507 533 phases = RhodeCodeUi()
508 534 phases.ui_section = 'phases'
509 535 phases.ui_key = 'publish'
510 536 phases.ui_value = False
511 537
512 538 sett1 = RhodeCodeSetting('realm', 'RhodeCode authentication')
513 539 sett2 = RhodeCodeSetting('title', 'RhodeCode')
514 540 sett3 = RhodeCodeSetting('ga_code', '')
515 541
516 542 sett4 = RhodeCodeSetting('show_public_icon', True)
517 543 sett5 = RhodeCodeSetting('show_private_icon', True)
518 544 sett6 = RhodeCodeSetting('stylify_metatags', False)
519 545
520 546 self.sa.add(web1)
521 547 self.sa.add(web2)
522 548 self.sa.add(web3)
523 549 self.sa.add(web4)
524 550 self.sa.add(paths)
525 551 self.sa.add(sett1)
526 552 self.sa.add(sett2)
527 553 self.sa.add(sett3)
528 554 self.sa.add(sett4)
529 555 self.sa.add(sett5)
530 556 self.sa.add(sett6)
531 557
532 558 self.create_ldap_options()
533 559
534 560 log.info('created ui config')
535 561
536 562 def create_user(self, username, password, email='', admin=False):
537 563 log.info('creating user %s' % username)
538 564 UserModel().create_or_update(username, password, email,
539 565 firstname='RhodeCode', lastname='Admin',
540 566 active=True, admin=admin)
541 567
542 568 def create_default_user(self):
543 569 log.info('creating default user')
544 570 # create default user for handling default permissions.
545 571 UserModel().create_or_update(username='default',
546 572 password=str(uuid.uuid1())[:8],
547 573 email='anonymous@rhodecode.org',
548 574 firstname='Anonymous', lastname='User')
549 575
550 576 def create_permissions(self):
551 577 # module.(access|create|change|delete)_[name]
552 578 # module.(none|read|write|admin)
553 579
554 580 for p in Permission.PERMS:
555 581 if not Permission.get_by_key(p[0]):
556 582 new_perm = Permission()
557 583 new_perm.permission_name = p[0]
558 584 new_perm.permission_longname = p[0]
559 585 self.sa.add(new_perm)
560 586
561 587 def populate_default_permissions(self):
562 588 log.info('creating default user permissions')
563 589
564 590 default_user = User.get_by_username('default')
565 591
566 592 for def_perm in ['hg.register.manual_activate', 'hg.create.repository',
567 593 'hg.fork.repository', 'repository.read']:
568 594
569 595 perm = self.sa.query(Permission)\
570 596 .filter(Permission.permission_name == def_perm)\
571 597 .scalar()
572 598 if not perm:
573 599 raise Exception(
574 600 'CRITICAL: permission %s not found inside database !!'
575 601 % def_perm
576 602 )
577 603 if not UserToPerm.query()\
578 604 .filter(UserToPerm.permission == perm)\
579 605 .filter(UserToPerm.user == default_user).scalar():
580 606 reg_perm = UserToPerm()
581 607 reg_perm.user = default_user
582 608 reg_perm.permission = perm
583 609 self.sa.add(reg_perm)
General Comments 0
You need to be logged in to leave comments. Login now