70b3caad620e893c42b25add75f341272d4fb3f1
[cascardo/ipsilon.git] / ipsilon / install / server.py
1 #!/usr/bin/python
2 #
3 # Copyright (C) 2014  Simo Sorce <simo@redhat.com>
4 #
5 # see file 'COPYING' for use and warranty information
6 #
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20 from ipsilon.login.common import LoginMgrsInstall
21 from ipsilon.providers.common import ProvidersInstall
22 import argparse
23 import cherrypy
24 import logging
25 import os
26 import shutil
27 import socket
28 import sys
29 import time
30
31
32 TEMPLATES = '/usr/share/ipsilon/templates/install'
33 CONFDIR = '/etc/ipsilon'
34 HTTPDCONFD = '/etc/httpd/conf.d'
35
36
37 class ConfigurationError(Exception):
38
39     def __init__(self, message):
40         super(ConfigurationError, self).__init__(message)
41         self.message = message
42
43     def __str__(self):
44         return repr(self.message)
45
46
47 #Silence cherrypy logging to screen
48 cherrypy.log.screen = False
49
50 # Regular logging
51 LOGFILE = '/var/log/ipsilon-install.log'
52 logger = logging.getLogger()
53
54
55 def openlogs():
56     global logger  # pylint: disable=W0603
57     if os.path.isfile(LOGFILE):
58         try:
59             created = '%s' % time.ctime(os.path.getctime(LOGFILE))
60             shutil.move(LOGFILE, '%s.%s' % (LOGFILE, created))
61         except IOError:
62             pass
63     logger = logging.getLogger()
64     try:
65         lh = logging.FileHandler(LOGFILE)
66     except IOError, e:
67         print >> sys.stderr, 'Unable to open %s (%s)' % (LOGFILE, str(e))
68         lh = logging.StreamHandler(sys.stderr)
69     formatter = logging.Formatter('[%(asctime)s] %(message)s')
70     lh.setFormatter(formatter)
71     logger.addHandler(lh)
72
73
74 def install(plugins, args):
75     logger.info('Installation initiated')
76     now = time.strftime("%Y%m%d%H%M%S", time.gmtime())
77
78     logger.info('Installing default config files')
79     ipsilon_conf = os.path.join(CONFDIR, 'ipsilon.conf')
80     idp_conf = os.path.join(CONFDIR, 'idp.conf')
81     httpd_conf = os.path.join(HTTPDCONFD, 'idp.conf')
82     if os.path.exists(ipsilon_conf):
83         shutil.move(ipsilon_conf, '%s.bakcup.%s' % (ipsilon_conf, now))
84     if os.path.exists(idp_conf):
85         shutil.move(idp_conf, '%s.backup.%s' % (idp_conf, now))
86     shutil.copy(os.path.join(TEMPLATES, 'ipsilon.conf'), CONFDIR)
87     shutil.copy(os.path.join(TEMPLATES, 'idp.conf'), CONFDIR)
88     if not os.path.exists(httpd_conf):
89         os.symlink(idp_conf, httpd_conf)
90     # Load the cherrypy config from the newly installed file so
91     # that db paths and all is properly set before configuring
92     # components
93     cherrypy.config.update(ipsilon_conf)
94
95     # Move pre-existing admin db away
96     admin_db = cherrypy.config['admin.config.db']
97     if os.path.exists(admin_db):
98         shutil.move(admin_db, '%s.backup.%s' % (admin_db, now))
99
100     logger.info('Configuring login managers')
101     for plugin_name in args['lm_order']:
102         plugin = plugins['Login Managers'][plugin_name]
103         plugin.configure(args)
104
105     logger.info('Configuring Authentication Providers')
106     for plugin_name in plugins['Auth Providers']:
107         plugin = plugins['Auth Providers'][plugin_name]
108         plugin.configure(args)
109
110
111 def uninstall(plugins, args):
112     logger.info('Uninstallation initiated')
113     raise Exception('Not Implemented')
114
115
116 def find_plugins():
117     plugins = {
118         'Login Managers': LoginMgrsInstall().plugins,
119         'Auth Providers': ProvidersInstall().plugins
120     }
121     return plugins
122
123
124 def parse_args(plugins):
125     parser = argparse.ArgumentParser(description='Ipsilon Install Options')
126     parser.add_argument('--version',
127                         action='version', version='%(prog)s 0.1')
128     parser.add_argument('-o', '--login-managers-order', dest='lm_order',
129                         help='Comma separated list of login managers')
130     parser.add_argument('--hostname',
131                         help="Machine's fully qualified host name")
132     parser.add_argument('--ipa', choices=['yes', 'no'], default='yes',
133                         help='Detect and use an IPA server for authentication')
134     parser.add_argument('--uninstall', action='store_true',
135                         help="Uninstall the server and all data")
136
137     lms = []
138
139     for plugin_group in plugins:
140         group = parser.add_argument_group(plugin_group)
141         for plugin_name in plugins[plugin_group]:
142             plugin = plugins[plugin_group][plugin_name]
143             if plugin.ptype == 'login':
144                 lms.append(plugin.name)
145             plugin.install_args(group)
146
147     args = vars(parser.parse_args())
148
149     if not args['hostname']:
150         args['hostname'] = socket.getfqdn()
151
152     if len(args['hostname'].split('.')) < 2:
153         raise ConfigurationError('Hostname: %s is not a FQDN')
154
155     if args['lm_order'] is None:
156         args['lm_order'] = []
157         for name in lms:
158             if args[name] == 'yes':
159                 args['lm_order'].append(name)
160     else:
161         args['lm_order'] = args['lm_order'].split(',')
162
163     if len(args['lm_order']) == 0:
164         #force the basic pam provider if nothing else is selected
165         if 'pam' not in args:
166             parser.print_help()
167             sys.exit(-1)
168         args['lm_order'] = ['pam']
169         args['pam'] = 'yes'
170
171     return args
172
173 if __name__ == '__main__':
174     opts = []
175     out = 0
176     openlogs()
177     try:
178         fplugins = find_plugins()
179         opts = parse_args(fplugins)
180
181         logger.setLevel(logging.DEBUG)
182
183         logger.info('Intallation arguments:')
184         for k in sorted(opts.iterkeys()):
185             logger.info('%s: %s', k, opts[k])
186
187         if 'uninstall' in opts and opts['uninstall'] is True:
188             uninstall(fplugins, opts)
189
190         install(fplugins, opts)
191     except Exception, e:  # pylint: disable=broad-except
192         logger.exception(e)
193         if 'uninstall' in opts and opts['uninstall'] is True:
194             print 'Uninstallation aborted.'
195         else:
196             print 'Installation aborted.'
197         print 'See log file %s for details' % LOGFILE
198         out = 1
199     finally:
200         if out == 0:
201             if 'uninstall' in opts and opts['uninstall'] is True:
202                 print 'Uninstallation complete.'
203             else:
204                 print 'Installation complete.'
205     sys.exit(out)