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