root/cleverbox/tags/0.3/cleverbox/scripts/admin.py @ 136

Revision 136, 39.6 KB (checked in by trivoallan, 3 years ago)

cleverbox : fixed tag version

Line 
1# -*- coding: utf-8 -*-
2
3import cmd
4import os
5import shlex
6import sys
7import traceback
8import re
9from trac import util
10from trac.scripts.admin import TracAdmin
11import ConfigParser
12
13_defaults = {
14    'env_dir_layout'       : ('clients-available', 'clients-enabled',
15                              'projects-available', 'projects-enabled'),
16    'client_dir_layout' : ('htdocs', 'logs', 'tmp', 'uploads',
17                           'var/svn', 'var/trac'),
18}
19
20_version = '0.3'
21
22class CleverboxAdmin(cmd.Cmd):
23    intro = ''
24    license = ''
25    doc_header = 'Cleverbox Admin Console \n' \
26                 'Available Commands:\n'
27    ruler = ''
28    prompt = "Cleverbox > "
29    envname = None
30    __env = None
31    _date_format = '%Y-%m-%d'
32    _datetime_format = '%Y-%m-%d %H:%M:%S'
33    _date_format_hint = 'YYYY-MM-DD'
34
35    _config = None
36
37    def __init__(self, envdir = None):
38        cmd.Cmd.__init__(self)
39        if envdir:
40            self.env_set(os.path.abspath(envdir))
41        self.interactive = False
42
43    ##
44    ## Environment methods
45    ##
46
47    def env_set(self, envname, env=None):
48        self.envname = envname
49        self.prompt = "Cleverbox [%s] > " % self.envname
50        if env is not None:
51            self.__env = env
52
53        # Read configuration
54        self._config = ConfigParser.SafeConfigParser()
55        self._config.read(os.path.join(self.envname, 'cleverbox.ini'))
56
57
58    def env_check(self):
59        try:
60            pass
61            #self.__env = Environment(self.envname)
62        except:
63            return 0
64        return 1
65
66    def env_open(self):
67        try:
68            if not self.__env:
69                #self.__env = Environment(self.envname)
70                return self.__env
71        except Exception, e:
72            print 'Failed to open environment.', e
73            traceback.print_exc()
74            sys.exit(1)
75
76    def emptyline(self):
77        pass
78
79    def onecmd(self, line):
80        try:
81            rv = cmd.Cmd.onecmd(self, line) or 0
82        except SystemExit:
83            raise
84        except Exception, e:
85            print >> sys.stderr, 'Command failed: %s' % e
86            rv = 2
87        if not self.interactive:
88            return rv
89
90    def run(self):
91        self.interactive = True
92        print 'Welcome to cleverbox-admin\n'                \
93              'Interactive Cleverbox administration console.\n'       \
94              "Type:  '?' or 'help' for help on commands.\n"
95        self.cmdloop()
96
97    #
98    # Configuration related methods
99    #
100    def getConfig(self, directive, section):
101        try:
102            val = self._config.get(section, directive)
103        except ConfigParser.NoOptionError, exception:
104            val = ''
105        return val
106
107    #
108    # Commands
109    #
110
111    ## Environment
112    _help_initenv = [('initenv', 'Environment initialisation')]
113    def do_initenv(self, line=None):
114        print "Environment initialisation in %(env_dir)s" % {'env_dir' : self.envname}
115
116        # Collect local configuration info
117        collected_infos = {}
118
119        # Path to client dir
120        default_root = '/var/cleverbox'
121        collected_infos['clients_root'] = raw_input('Projects directory [%s]> ' % default_root).strip() or default_root
122
123        try :
124            os.makedirs(collected_infos['clients_root'], 0775)
125        except IOError, ioexception :
126            print ioexception
127            print "** Projects storage dir could not be created."
128
129        # Apache user & group
130        d_uid = 33
131        d_gid = 33
132        collected_infos['apache_user'] = raw_input('Webserver user id [%d]> ' % d_uid).strip() or d_uid
133        collected_infos['apache_group'] = raw_input('Webserver group id [%d]> ' % d_gid).strip() or d_gid
134
135        # SSH user & group
136        d_uid = '1001'
137        d_gid = '1001'
138        collected_infos['ssh_user'] = raw_input('SSH user id [%s]> ' % d_uid).strip() or d_uid
139        collected_infos['ssh_group'] = raw_input('SSH group id [%s]> ' % d_gid).strip() or d_gid
140
141        # Virtual Host mode : single or multi ?
142        d_vhost_mode = 'single'
143        collected_infos['vhost_mode'] = raw_input('Virtual host mode (either "single" or "multi") [%s]> ' % d_vhost_mode).strip() or d_vhost_mode
144       
145        if collected_infos['vhost_mode'] == 'multi':
146            collected_infos['domain'] = raw_input('Domain name > ').strip()
147       
148        # Cleverbox assets
149        d_cleverbox_assets = '/usr/share/cleverbox'
150        collected_infos['assets_dir'] = raw_input('Cleverbox assets directory [%s]> ' % d_cleverbox_assets).strip() or d_cleverbox_assets
151
152        # Authentication backend password (if any)
153        collected_infos['authbackend_pass'] = raw_input('Authentication backend password (if any) []> ').strip() or ''
154               
155        # Write ini file
156        self._config.add_section('general')
157        for directive, value in collected_infos.items() :
158            self._config.set('general', directive, str(value))
159
160        trac_infos = {}
161        d_lib_dir = '/usr/share/python-support/trac'
162        trac_infos['lib_dir'] = raw_input('Trac libs directory [%s]> ' % d_lib_dir).strip() or d_lib_dir
163
164        d_assets_dir = '/usr/share/trac'
165        trac_infos['assets_dir'] = raw_input('Trac assets directory [%s]> ' % d_assets_dir).strip() or d_assets_dir
166
167        self._config.add_section('trac')
168        for directive, value in trac_infos.items() :
169            self._config.set('trac', directive, str(value))
170       
171        fh_config = open(os.path.join(self.envname, 'cleverbox.ini'), 'w+')
172        self._config.write(fh_config)
173
174        # Create directory structure
175        try:
176            print "\n\tCreating directory structure\n"
177            env_dirs = []
178            for dirname in _defaults['env_dir_layout']:
179                env_dirs.append( os.path.join(self.envname, dirname) )
180            map( os.makedirs, env_dirs )
181        except IOError, ioexception :
182            print ioexception
183            print "** Environment couldn't be initialized in %(env_dir)s\n" % {'env_dir' : self.envname}
184
185        # Create VERSION file
186        try:
187            fd = open( os.path.join(self.envname, 'VERSION'), 'w' )
188            fd.write(_version)
189        finally:
190            fd.close()
191
192        print
193        print "Environment successfully initialized\n"
194        print "You need to add this statement to your apache configuration : \n" \
195            "\t'Include %(env_dir)s/clients-enabled/*'\n" % {'env_dir' : self.envname}
196
197    ## help
198    def do_help(self, line=None):
199        arg = self._arg_tokenize(line)
200
201        if arg[0]:
202            try:
203                doc = getattr(self, "_help_" + arg[0])
204                TracAdmin.print_doc(doc)
205            except AttributeError:
206                print "No documentation found for %s" % arg[0]
207        else:
208            docs = (self._help_client + self._help_project + self._help_initenv)
209            print 'cleverbox-admin - The Cleverbox Administration Console'
210            if not self.interactive:
211                print
212                print "Usage: cleverbox-admin env_dir [command [subcommand] [option ...]]\n"
213                print "Invoking cleverbox-admin without command starts "\
214                      "interactive mode."
215            TracAdmin.print_doc(docs)
216
217
218    #
219    # "Client" command set
220    #
221    _help_client = [('client list', 'Lists available clients'),
222                    ('client add <name>', 'Adds a client'),
223                    ('client remove <name>', 'Deletes a client'),
224                    ('client enable <name>', 'Enables a client'),
225                    ('client disable <name>', 'Disables a client')]
226    def do_client(self, line):
227        args = self._arg_tokenize(line)
228        if args[0] == 'list':
229            self._do_client_list()
230        elif args[0] == 'add' and len(args) == 2:
231            self._do_client_add(args[1])
232        elif args[0] == 'remove' and len(args) == 2:
233            self._do_client_remove(args[1])
234        elif args[0] == 'enable' and len(args) == 2:
235            self._do_client_enable(args[1])
236        elif args[0] == 'disable' and len(args) == 2:
237            self._do_client_disable(args[1])
238        else:
239            self.do_help('client')
240
241
242    def complete_client(self, text, line, begidx, endidx):
243        '''
244        Completions for the client command.
245        Each client subcommand has its own completion command, named _complete_client_<subcommand>
246        '''
247
248        comp = ['list', 'add', 'remove', 'enable', 'disable']
249        parts = self._arg_tokenize(line)
250
251        if len(parts) > 1 and parts[1] in comp:
252            complete_func = getattr(self, '_complete_client_' + parts[1])
253            if (float(sys.version[:3]) < 2.4):
254                comp = complete_func.__call__(self)
255            else:
256                comp = complete_func.__call__()
257
258        return self.word_complete( text, comp )
259
260
261    def _do_client_list(self):
262        '''
263        Displays enabled and disabled clients.
264        '''
265
266        print "Enabled :\n" \
267              "%s\n\n" \
268              "Disabled : \n" \
269              "%s\n" % (self._get_clients('enabled'), self._get_clients('disabled'))
270
271    def _do_client_add(self, client_name):
272        """
273        Creates a new client.
274          * Checks if client not already exists.
275          * Collects informations about client
276          * Checks informations are correct
277          * Create clients environment directory layout
278          * Creates apache conf for apache
279          * Inserts client into LDAP
280        """
281
282        if not self._client_exists(client_name):
283
284            # Client name cannot contain strokes
285            if client_name.count('-') != 0:
286                print "** Client's identifier can not contain the '-' character."
287                return
288
289            # Information collection
290            collected_infos = { 'full_name'    : None,
291                                'home_dir'     : None,
292                                'ftp_password' : None }
293
294            collected_infos['home_dir'] = os.path.join(self.getConfig('clients_root', 'general'), client_name)
295
296            # A few checks before effectively creating client
297            # -- homedir
298            parent_dir = os.path.normpath(os.path.join(collected_infos['home_dir'], os.pardir))
299            dir_is_ok = os.access(parent_dir, os.W_OK) and not os.access(collected_infos['home_dir'], os.R_OK)
300            if not dir_is_ok:
301                print "Directory %s is not writable or already exists, aborting." % collected_infos['home_dir']
302                raise os.error
303
304
305            print
306            print "Creating a new client."
307            print "Let's collect some informations about him : "
308            print
309            print "  Please enter client's full name."
310            print "  This will be used in several interface screens and in emails."
311            print
312
313            dfn = client_name.capitalize()
314            collected_infos['full_name'] = raw_input('Full Name [%s]> ' % dfn).strip() or dfn
315
316            print
317            print "Supplied informations verified."
318            print "Let's proceed with client creation :"
319            print
320
321            # Client creation
322            # -- Homedir layout
323            homedir_layout = []
324            for d in _defaults['client_dir_layout']:
325                homedir_layout.append( os.path.join(collected_infos['home_dir'], d) )
326            map( os.makedirs, homedir_layout )
327
328            print "  Directory layout created in %s\n" % collected_infos['home_dir']
329
330            # -- Apache configuration
331            apache_conf = """
332# -- Include enabled projects configuration files
333Include %(env_dir)s/projects-enabled/%(client_name)s-*
334""" % { 'client_name' : client_name,
335        'home_dir'    : collected_infos['home_dir'],
336        'env_dir'     : self.envname }
337
338            # -- Write conf to filesystem
339            apache_conf_filepath = os.path.join( self.envname, 'clients-available', client_name )
340            f = file(apache_conf_filepath, 'w+')
341            f.write(apache_conf)
342            f.close()
343
344            print "  Apache configuration written to %s\n" % apache_conf_filepath
345
346            # -- Fix permissions
347            """
348            chown -R dev:dev /$CLIENTSROOT/$CLIENTNAME
349            chown dev:www-data /$CLIENTSROOT/$CLIENTNAME/logs
350            chmod g+w /$CLIENTSROOT/$CLIENTNAME/logs
351            chown dev:www-data /$CLIENTSROOT/$CLIENTNAME/uploads
352            chmod g+w /$CLIENTSROOT/$CLIENTNAME/uploads
353            """
354            self._rchown( collected_infos['home_dir'],
355                         int(self.getConfig('ssh_user', 'general')),
356                         int(self.getConfig('ssh_group', 'general')) )
357            os.chown( os.path.join(collected_infos['home_dir'], 'logs'),
358                     int(self.getConfig('ssh_user', 'general')), int(self.getConfig('apache_group', 'general')) )
359            os.chmod( os.path.join(collected_infos['home_dir'], 'logs'), 0775 )
360            os.chown( os.path.join(collected_infos['home_dir'], 'uploads'),
361                     int(self.getConfig('ssh_user', 'general')), int(self.getConfig('apache_group', 'general')) )
362            os.chmod( os.path.join(collected_infos['home_dir'], 'uploads'), 0775 )
363
364            print "  Fixed permissions in %s\n" % collected_infos['home_dir']
365
366            #  Final steps
367            # -- Enable client ?
368            print "Client has been created. Do you want to enable it ?"
369            print
370            enable_client = raw_input('Enable client ? [y/N]> ').strip() or False
371
372            if enable_client == 'y':
373                self._do_client_enable(client_name)
374
375            print
376            print "Client %s was successfully created." % collected_infos['full_name']
377            print "Apache configuration need to be reloaded for this to be effective."
378            print
379            print "You may want create some projects now. Try 'help project' for some documentation."
380            print
381
382        else:
383            print "This client already exists !"
384
385    def _do_client_remove(self, client_name):
386        """
387        Suppression d'un client.
388        Un client actif ne peut être supprimé.
389        TODO : suppression du répertoire du client. (bof)
390        """
391
392        if self._client_exists(client_name):
393            continue_removal = False
394            print "You are about to remove client '%s'." % client_name
395            confirm_removal = raw_input('Remove client ? [y/N]> ').strip() or False
396            if confirm_removal == 'y':
397                continue_removal = True
398
399            if not continue_removal:
400                print "Client '%s' was *not* removed" % client_name
401                return
402
403            # Client is enabled, propose disabling it
404            client_disabled = True
405            if client_name in self._get_clients('enabled'):
406                client_disabled = False
407                print "You can not remove an enabled client. Disabling it."
408                try:
409                    self._do_client_disable(client_name)
410                except Exception, e:
411                    print e
412
413            # Disable client's projects
414            for project_name in self._get_projects(client_name, 'enabled'):
415                self._do_project_disable((client_name, project_name))
416
417            # Disable client
418            client_apacheconf = os.path.join( self.envname, 'clients-available', client_name )
419            os.unlink(client_apacheconf)
420            print "Client '%s' has been removed." % client_name
421            print "Apache configuration needs to be reloaded for this to be effective."
422        else:
423            print "This client does not exist !"
424
425    def _do_client_enable(self, client_name):
426        enable_client = True
427        if not self._client_exists(client_name):
428            print "This client does not exist !"
429            enable_client = False
430
431        if client_name in self._get_clients('enabled'):
432            print "This client is already enabled !"
433            enable_client = False
434
435        if enable_client:
436            target = os.path.join( self.envname, 'clients-available', client_name )
437            linkname = os.path.join( self.envname, 'clients-enabled', client_name )
438            os.symlink( target, linkname )
439            print "Client '%s' has been enabled" % client_name
440            print "Apache configuration needs to be reloaded for this to be effective."
441        else:
442            print "Client '%s' was *not* enabled" % client_name
443
444    def _do_client_disable(self, client_name):
445        """
446        Disables a client.
447        TODO : disable client's projects.
448        """
449        disable_client = True
450        if not self._client_exists(client_name):
451            print "This client does not exist !"
452            disable_client = False
453
454        if client_name in self._get_clients('disabled'):
455            print "This client is already disabled !"
456            disable_client = False
457
458        if disable_client:
459            linkname = os.path.join( self.envname, 'clients-enabled', client_name )
460            os.unlink( linkname )
461            print "Client '%s' has been disabled" % client_name
462            print "Apache configuration needs to be reloaded for this to be effective."
463        else:
464            print "Client '%s' was *not* disabled" % client_name
465
466
467    def _complete_client_remove(self):
468        return self._get_clients()
469
470    def _complete_client_enable(self):
471        return self._get_clients('disabled')
472
473    def _complete_client_disable(self):
474        return self._get_clients('enabled')
475
476
477    def _client_exists(self, name):
478        """
479        Tells whether or not a client exists.
480        return bool
481        """
482        return name in self._get_clients()
483
484    def _get_clients(self, only = False):
485        '''
486        Returns a list of clients.
487        The "only" parameter is used to restrict returned clients list. Recognized values are "enabled" and "disabled".
488        '''
489
490        list_clients = []
491
492        # Each client
493        if not only:
494            list_clients = os.listdir(os.path.join(self.envname, 'clients-available'))
495
496        # Only enabled clients
497        elif only == 'enabled':
498            list_clients = os.listdir(os.path.join(self.envname, 'clients-enabled'))
499
500        # Only disabled clients (ie. not enabled)
501        elif only == 'disabled':
502
503            disabled_clients = []
504            available_clients = os.listdir(os.path.join(self.envname, 'clients-available'))
505            enabled_clients = os.listdir(os.path.join(self.envname, 'clients-enabled'))
506
507            # Expect poor performances for this intersection algorithm
508            # see http://python.project.cwi.nl/search/hypermail/python-recent/0159.html
509            for e in available_clients:
510                if e not in enabled_clients:
511                    disabled_clients.append(e)
512
513            list_clients = disabled_clients
514
515        return list_clients
516
517
518    #
519    # Project command set
520    #
521
522    _help_project = [('project list', 'Lists available projects'),
523                    ('project add <client> <name>', 'Adds a project'),
524                    ('project remove <client> <name>', 'Deletes a project'),
525                    ('project enable <client> <name>', 'Enables a project'),
526                    ('project disable <client> <name>', 'Disables a project')]
527    def do_project(self, line):
528        parts = self._arg_tokenize(line)
529        try:
530            do_func = getattr(self, '_do_project_' + parts[0])
531            # Python __call__() signature changed in Python2.4
532            if ( sys.version[:3] == '2.4'):
533                do_func.__call__(parts[1:])
534            else:
535                do_func.__call__(self, parts[1:])
536        except AttributeError, e:
537            print e
538            self.do_help('project')
539
540    def _do_project_list(self, line=None):
541        """
542        TODO : Nicer formatting
543        """
544
545        print
546        print "Available projects :"
547        print "--------------------"
548
549        client_projects = {}
550
551        clients = self._get_clients()
552        for client_name in clients:
553            client_projects[client_name] = self._get_projects(client_name)
554
555        print client_projects
556
557
558    def _project_exists(self, client_name, project_name, project_status = None):
559        return project_name in self._get_projects(client_name, project_status)
560
561    def _get_projects(self, client_name = None, status = None):
562        """
563        TODO : Extend command options in order to provide better filtering (client, enabled / disabled)
564        """
565        list_projects = []
566
567        # -- List of requested projects
568        enabled_projects_dir = 'projects-enabled'
569        all_projects_dir     = 'projects-available'
570
571        list_enabled_projects = os.listdir(os.path.join(self.envname, enabled_projects_dir))
572        list_all_projects = os.listdir(os.path.join(self.envname, all_projects_dir))
573
574        list_projects = []
575        if (status == None):
576            list_projects = list_all_projects
577        if (status == 'enabled'):
578            list_projects = list_enabled_projects
579        if (status == 'disabled'):
580            for p in list_all_projects:
581                if p not in list_enabled_projects:
582                    list_projects.append(p)
583
584        # Restrict to requested client
585        pattern = re.compile('(\w+)-(.*)')
586        if (client_name):
587            client_projects = []
588            for project_name in list_projects:
589                matches = pattern.findall(project_name)
590                if (client_name == matches[0][0]):
591                    client_projects.append(matches[0][1])
592            list_projects = client_projects
593
594        return list_projects
595
596    def _do_project_disable(self, args):
597        if len(args) == 2:
598            (client_name, project_name) = args
599            if (self._project_exists(client_name, project_name)):
600                try:
601                    os.unlink( os.path.join(self.envname, 'projects-enabled', '%s-%s' % (client_name, project_name)) )
602                    print "Project '%s' has been disabled. Apache needs to be reloaded." % project_name
603                except (IOError, os.error), exception:
604                        print "** Project '%s' is already disabled." % project_name
605
606            else:
607                print "** Client '%s' has no project '%s'" % (client_name, project_name)
608        else:
609            print "** Invalid syntax"
610            self.do_help('project')
611
612    def _do_project_enable(self, args):
613        if len(args) == 2:
614            (client_name, project_name) = args
615            if self._project_exists(client_name, project_name):
616                try:
617                    target = os.path.join( self.envname,
618                                          'projects-available',
619                                          '%s-%s' % (client_name, project_name) )
620                    linkname = os.path.join( self.envname,
621                                          'projects-enabled',
622                                          '%s-%s' % (client_name, project_name) )
623                    os.symlink( target, linkname )
624                    print "Project '%s' has been enabled. Apache needs to be reloaded." % project_name
625                except Exception, e:
626                    print e
627
628            else:
629                print "** Client '%s' has no project '%s'" % (client_name, project_name)
630        else:
631            print "** Invalid syntax"
632            self.do_help('project')
633
634    def _do_project_remove(self, args):
635        print "** TODO : This command has yet to be implemented."
636
637        (client_name, project_name) = args
638
639        # Confirm deletion
640        continue_removal = False
641        print "You are about to remove project '%s'." % project_name
642        confirm_removal = raw_input('Remove project ? [y/N]> ').strip() or False
643        if confirm_removal == 'y':
644            continue_removal = True
645
646        if not continue_removal:
647            print "Project '%s' was *not* removed\n" % project_name
648            return
649
650        # Disable project
651        if self._project_exists(client_name, project_name, 'enabled'):
652            print "You cannot remove an enabled project. Disabling it."
653            self._do_project_disable(args)
654
655        # Delete apache config file
656        try:
657            linkname = os.path.join( self.envname, 'projects-available', '%s-%s'% (client_name, project_name) )
658            os.unlink( linkname )
659            print "Project '%s' has been removed" % project_name
660            print "Apache configuration needs to be reloaded for this to be effective."
661        except (IOError, os.error), exception:
662            print "** An error occured. Project was NOT removed. Report the following message to the sysadmin."
663            print exception
664
665    def _do_project_add(self, args):
666        if len(args) == 2:
667            client_name = args[0]
668            project_name = args[1]
669
670            # Make sure client exists
671            if not self._client_exists(client_name):
672                print "Client '%s' does not exist !" % client_name
673                return
674            # Make sure project does not already exists
675            if self._project_exists(client_name, project_name):
676                print "Client '%s' already has a project named '%s'" % (client_name, project_name)
677                return
678
679            # Information collection
680            collected_infos = {
681                'client'     : client_name,
682                'short_name' : project_name,
683                'full_name'  : None
684                }
685
686            print
687            print "Creating a new project for client '%s'" % client_name
688            print "Let's collect some informations about the project :"
689            print
690            print "  Please enter project's full name."
691            print "  This will be displayed in various places and emails."
692            print
693
694            dfn = project_name.capitalize()
695            collected_infos['full_name'] = raw_input('Full Name [%s]> ' % dfn).strip() or dfn
696
697            # Project creation
698            print
699            print "Supplied informations verified."
700            print "Let's proceed with project creation :"
701            print
702
703            try:
704                # -- Apache configuration file
705                self._project_write_apache_conf( collected_infos )
706
707                # -- Create required dirs
708                self._project_create_dirs( collected_infos )
709
710                # -- SVN repository creation
711                self._project_create_svn_repos( collected_infos )
712
713                # -- Trac environment initialisation
714                self._project_trac_initenv( collected_infos )
715
716                # -- Trac initial permissions
717                self._project_trac_setperms( collected_infos )
718
719                # -- Modifies Trac default conf
720                self._project_trac_defaultconf( collected_infos )
721               
722                # -- Fix perms
723                self._project_fix_perms( collected_infos )
724
725            except Exception, e:
726                print "An error occured :"
727                print e
728                traceback.print_exc()
729
730            # -- Enable client ?
731            print "Project has been created. Do you want to enable it ?"
732            print
733            enable_project = raw_input('Enable project ? [y/N]> ').strip() or False
734
735            if enable_project.find('y') == 0:
736                self._do_project_enable((client_name, project_name))
737
738        else:
739            self.do_help('project')
740
741    def _project_create_dirs(self, infos):
742        os.makedirs(os.path.join(self.getConfig('clients_root', 'general'),
743                                 infos['client'],
744                                 'htdocs',
745                                 infos['short_name']), 0775)
746
747        print "  Creating project's directory layout\n"
748
749    def _project_write_apache_conf(self, infos):
750        """
751        TODO : clients may not be stored in 'clients_root', be careful with that.
752        """
753        conf_template = open('%s/%s.%s' % (self.getConfig('assets_dir', 'general'), 'apache.conf', self.getConfig('vhost_mode', 'general')))
754        conf_data = conf_template.read() % {'client_name'      : infos['client'],
755                                            'project_name'     : infos['short_name'],
756                                            'clients_root'     : self.getConfig('clients_root', 'general'),
757                                            'authbackend_pass' : self.getConfig('authbackend_pass', 'general'),
758                                            'trac_install_dir' : self.getConfig('lib_dir', 'trac'),
759                                            'domain_name'      : self.getConfig('domain', 'general')}
760
761        apache_conf_filepath = os.path.join( self.envname, 'projects-available', '%s-%s' % (infos['client'], infos['short_name']) )
762        f = file(apache_conf_filepath, 'w+')
763        f.write(conf_data)
764        f.close()
765
766        print "  Apache configuration written to %s\n" % apache_conf_filepath
767
768    def _project_create_svn_repos(self, infos):
769        repos_path = os.path.join( self.getConfig('clients_root', 'general'), infos['client'], 'var/svn', infos['short_name'] )
770        create_cmd = 'svnadmin create %s' % repos_path
771        (stdin, stdout, stderr) = os.popen3( create_cmd )
772
773        err = stderr.read()
774        if err:
775            raise Exception( err )
776
777        print "  Subversion repository created in %s\n" % repos_path
778
779    def _project_trac_initenv(self, infos):
780
781        trac_env_path = os.path.join( self.getConfig('clients_root', 'general'),
782                                     infos['client'],
783                                     'var/trac',
784                                     infos['short_name'] )
785
786        svn_path = os.path.join( self.getConfig('clients_root', 'general'),
787                                infos['client'],
788                                'var/svn',
789                                infos['short_name'] )
790       
791        cmd_data = { 'env_path'         : trac_env_path,
792                     'title'            : '"%s - %s - Trac"' % (infos['client'], infos['short_name']),
793                     'db_dsn'           : 'sqlite:db/trac.db',
794                     'svn_path'         : svn_path,
795                     'templates_path'   : '%s/templates' % self.getConfig('assets_dir', 'trac'),
796                     'trac_install_dir' : self.getConfig('lib_dir', 'trac')}
797
798        trac_cmd = '/usr/bin/trac-admin %(env_path)s initenv %(title)s %(db_dsn)s svn %(svn_path)s %(templates_path)s' % cmd_data
799       
800        (stdin, stdout, stderr) = os.popen3( trac_cmd )
801
802        err = stderr.read()
803        if err:
804            err = stdout.read()
805            raise Exception( err )
806
807        print "  Trac project initialized in %s\n" % trac_env_path
808
809    def _project_trac_setperms(self, infos):
810        trac_env_path = os.path.join( self.getConfig('clients_root', 'general'),
811                                     infos['client'],
812                                     'var/trac',
813                                     infos['short_name'] )
814
815        # Perms for authenticated users
816        default_perms = ( 'BROWSER_VIEW',
817                          'CHANGESET_VIEW',
818                          'FILE_VIEW',
819                          'LOG_VIEW',
820                          'MILESTONE_VIEW',
821                          'REPORT_SQL_VIEW',
822                          'REPORT_VIEW',
823                          'ROADMAP_VIEW',
824                          'SEARCH_VIEW',
825                          'TICKET_CREATE',
826                          'TICKET_MODIFY',
827                          'TICKET_VIEW',
828                          'TIMELINE_VIEW',
829                          'WIKI_CREATE',
830                          'WIKI_MODIFY',
831                          'WIKI_VIEW' )
832
833        trac_perms_cmd = 'trac-admin %(env_path)s permission %(subcommand)s %(subject)s %(perms)s'
834
835        # Remove all anonymous permissions
836        os.system( trac_perms_cmd % {'env_path'   : trac_env_path,
837                                     'subcommand' : 'remove',
838                                     'subject'    : 'anonymous',
839                                     'perms'      : ' '.join(default_perms)} )
840
841        # Grant default permissions
842        perms_config = ConfigParser.SafeConfigParser()
843        perms_config.read(os.path.join(self.getConfig('assets_dir', 'general'), 'permissions.ini'))
844        for profile in perms_config.options('trac'):
845            os.system( trac_perms_cmd % {'env_path'   : trac_env_path,
846                                         'subcommand' : 'add',
847                                         'subject'    : profile,
848                                         'perms'      : perms_config.get('trac', profile)} )
849
850        # Let user choose who gets admin rights
851        print "  Please enter trac administrator username."
852        print "  This user will be granted full privileges on project's Trac instance."
853        print
854
855        dan = infos['short_name'] + '-admin'
856        admin_login = raw_input('Admin Login [%s]> ' % dan).strip() or dan
857
858        os.system( trac_perms_cmd % {'env_path'   : trac_env_path,
859                                     'subcommand' : 'add',
860                                     'subject'    : admin_login,
861                                     'perms'      : 'TRAC_ADMIN'} )
862        print
863        print "  Trac initial permissions set (admin rights given to '%s')\n" % admin_login
864
865    def _project_fix_perms(self, infos):
866        """
867        chown -R dev:www-data /$CLIENTSROOT/$CLIENTNAME/var/svn/$PROJECTNAME
868        chmod g+w /$CLIENTSROOT/$CLIENTNAME/var/svn/$PROJECTNAME
869        chown -R www-data:dev /$CLIENTSROOT/$CLIENTNAME/var/trac/$PROJECTNAME/db
870        chown dev:www-data /$CLIENTSROOT/$CLIENTNAME/var/trac/conf/trac.ini
871        chmod g+w /$CLIENTSROOT/$CLIENTNAME/var/trac/conf/trac.ini
872        """
873
874        # Trac DB
875        trac_db_path   = os.path.join( self.getConfig('clients_root', 'general'),
876                                      infos['client'],
877                                      'var/trac',
878                                      infos['short_name'],
879                                      'db' )
880        self._rchown( trac_db_path , int(self.getConfig('apache_user', 'general')), int(self.getConfig('ssh_group', 'general')) )
881
882        # Trac attachments
883        trac_attachments_path = os.path.join( self.getConfig('clients_root', 'general'),
884                                             infos['client'],
885                                             'var/trac',
886                                             infos['short_name'],
887                                             'attachments' )
888
889        self._rchown( trac_attachments_path , int(self.getConfig('apache_user', 'general')), int(self.getConfig('ssh_group', 'general')) )
890
891        # Trac conf
892        trac_ini_path   = os.path.join( self.getConfig('clients_root', 'general'),
893                                       infos['client'],
894                                       'var','trac',
895                                       infos['short_name'],
896                                       'conf','trac.ini' )
897        os.chmod( trac_ini_path, 0777 )
898
899        # Subversion repository
900        svn_repos_path = os.path.join( self.getConfig('clients_root', 'general'),
901                                      infos['client'],
902                                      'var/svn',
903                                      infos['short_name'] )
904
905        self._rchown( svn_repos_path, int(self.getConfig('apache_user', 'general')), int(self.getConfig('ssh_group', 'general')) )
906        os.chmod( svn_repos_path, 0775 )
907
908        print "  Perms fixed\n"
909
910    def _project_trac_defaultconf(self, infos):
911        # New defaults
912        tracdefaults_config = ConfigParser.SafeConfigParser()
913        tracdefaults_config.read(os.path.join(self.getConfig('assets_dir', 'general'), 'trac-defaults.ini'))
914       
915        # Trac base config file
916        project_config_path = os.path.join('%s/%s/var/trac/%s/conf/trac.ini' % (self.getConfig('clients_root', 'general'), infos['client'], infos['short_name']))
917        tracproject_config = ConfigParser.SafeConfigParser()
918        tracproject_config.read(project_config_path)
919
920        # Overriding
921        for section in tracdefaults_config.sections():
922            if not tracproject_config.has_section(section):
923                tracproject_config.add_section(section)
924            for option in tracdefaults_config.options(section):
925                tracproject_config.set(section, option, tracdefaults_config.get(section, option))
926
927        fp = open(project_config_path, 'w+')
928        tracproject_config.write(fp)
929
930    def complete_project(self, text, line, begidx, endidx):
931        comp = ['list', 'add', 'remove', 'enable', 'disable']
932        parts = self._arg_tokenize(line)
933
934        if len(parts) > 1 and parts[1] in comp:
935            complete_func = getattr(self, '_complete_project_' + parts[1])
936            if (float(sys.version[:3]) < 2.4):
937                comp = complete_func.__call__(self, parts[2:])
938            else:
939                comp = complete_func.__call__(parts[2:])
940
941        return self.word_complete(text, comp)
942
943    def _complete_project_add(self, args):
944        comp = []
945
946        # "add" subcommand's first argument is client's name
947        if not args or (len(args) <= 1 and args[0] not in self._get_clients()):
948            comp = self._get_clients()
949
950        return comp
951
952    def _complete_project_enable(self, args):
953        comp = []
954
955        # "enable" subcommand's first argument is client's name
956        if not args or (len(args) <= 1 and args[0] not in self._get_clients()):
957            comp = self._get_clients()
958        # "enable" subcommand second argument is project's name
959        else :
960            comp = self._get_projects(args[0], 'disabled')
961
962        return comp
963
964    def _complete_project_disable(self, args):
965        comp = []
966
967        # "disable" subcommand's first argument is client's name
968        if not args or (len(args) <= 1 and args[0] not in self._get_clients()):
969            comp = self._get_clients()
970        # "disable" subcommand second argument is project's name
971        else :
972            comp = self._get_projects(args[0], 'enabled')
973
974        return comp
975
976    def _complete_project_remove(self, args):
977        comp = []
978
979        # "remove" subcommand's first argument is client's name
980        if not args or (len(args) <= 1 and args[0] not in self._get_clients()):
981            comp = self._get_clients()
982        # "remove" subcommand second argument is project's name
983        else :
984            comp = self._get_projects(args[0])
985
986        return comp
987
988    ## Quit / EOF
989    def do_quit(self, line):
990        print
991        sys.exit()
992
993    do_exit = do_quit # Alias
994    do_EOF = do_quit # Alias
995
996
997
998    #
999    # Helpers
1000    #
1001
1002    def word_complete (self, text, words):
1003        """
1004        Word completion helper.
1005        """
1006        return [a for a in words if a.startswith (text)]
1007
1008    def _rchown(self, directory, uid, gid):
1009        """
1010        Recursive chown.
1011        """
1012        try:
1013            if (os.path.isdir(directory)):
1014                os.chown(directory, uid, gid)
1015                direntries = os.listdir(directory)
1016                for direntry in direntries:
1017                    direntry = os.path.join(directory, direntry)
1018                    if (os.path.isdir(direntry)):
1019                        os.chown(direntry, uid, gid)
1020                        os.chdir(direntry)
1021                        self._rchown(direntry, uid, gid)
1022                    else:
1023                        os.chown(direntry, uid, gid)
1024            else:
1025                os.chown(directory, uid, gid)
1026        except (IOError, os.error), why:
1027            print "rchown %s: %s" % (str(directory), str(why))
1028
1029    def _arg_tokenize (self, argstr):
1030        """
1031        Command line parameters tokenizer
1032        """
1033        argstr = util.to_utf8(argstr, sys.stdin.encoding)
1034        return shlex.split(argstr) or ['']
1035
1036def run(args):
1037    """Main entry point."""
1038    admin = CleverboxAdmin()
1039
1040    if len(args) > 0:
1041        if args[0] in ('-h', '--help', 'help'):
1042            return admin.onecmd("help")
1043        elif args[0] in ('-v','--version','about'):
1044            return admin.onecmd("about")
1045        else:
1046            admin.env_set(os.path.abspath(args[0]))
1047            if len(args) > 1:
1048                s_args = ' '.join(["'%s'" % command_parts for command_parts in args[2:]])
1049                command = args[1] + ' ' +s_args
1050                return admin.onecmd(command)
1051            else:
1052                while True:
1053                    admin.run()
1054    else:
1055        return admin.onecmd("help")
1056
Note: See TracBrowser for help on using the browser.