##############################################################################
#
# Copyright (c) 2010 Vifib SARL and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
##############################################################################
import slapos.recipe.erp5
import os
import pkg_resources
import zc.buildout
import sys

class Recipe(slapos.recipe.erp5.Recipe):
  
  default_bt5_list = []

  def installKeyAuthorisationApache(self, ip, port, backend, key, certificate,
      ca_conf, key_auth_path='/erp5/portal_slap'):
    ssl_template = """SSLEngine on
SSLVerifyClient require
RequestHeader set REMOTE_USER %%{SSL_CLIENT_S_DN_CN}s
SSLCertificateFile %(key_auth_certificate)s
SSLCertificateKeyFile %(key_auth_key)s
SSLCACertificateFile %(ca_certificate)s
SSLCARevocationPath %(ca_crl)s"""
    apache_conf = self._getApacheConfigurationDict('key_auth_apache', ip, port)
    apache_conf['ssl_snippet'] = ssl_template % dict(
        key_auth_certificate=certificate,
        key_auth_key=key,
        ca_certificate=ca_conf['ca_certificate'],
        ca_crl=ca_conf['ca_crl']
        )
    prefix = 'ssl_key_auth_apache'
    rewrite_rule_template = \
      "RewriteRule (.*) http://%(backend)s%(key_auth_path)s$1 [L,P]"
    path_template = pkg_resources.resource_string('slapos.recipe.erp5',
      'template/apache.zope.conf.path.in')
    path = path_template % dict(path='/')
    d = dict(
          path=path,
          backend=backend,
          backend_path='/',
          port=apache_conf['port'],
          vhname=path.replace('/', ''),
          key_auth_path=key_auth_path,
    )
    rewrite_rule = rewrite_rule_template % d
    apache_conf.update(**dict(
      path_enable=path,
      rewrite_rule=rewrite_rule
    ))
    apache_config_file = self.createConfigurationFile(prefix + '.conf',
        pkg_resources.resource_string('slapos.recipe.erp5',
          'template/apache.zope.conf.in') % apache_conf)
    self.path_list.append(apache_config_file)
    self.path_list.extend(zc.buildout.easy_install.scripts([(
      'key_auth_apache',
        'slapos.recipe.erp5.apache', 'runApache')], self.ws,
          sys.executable, self.wrapper_directory, arguments=[
            dict(
              required_path_list=[certificate, key, ca_conf['ca_certificate'],
                ca_conf['ca_crl']],
              binary=self.options['httpd_binary'],
              config=apache_config_file
            )
          ]))
    return 'https://%(ip)s:%(port)s' % apache_conf

  def _getZeoClusterDict(self):
    site_path = '/erp5/'
    return {
        '/': (self._requestZeoFileStorage('Zeo Server 1', 'main'),
          site_path + 'account_module'),
    }

  def installProduction(self):
    ca_conf = self.installCertificateAuthority(
        self.parameter_dict['ca_country_code'],
        self.parameter_dict['ca_email'], self.parameter_dict['ca_state'],
        self.parameter_dict['ca_city'], self.parameter_dict['ca_company'])
    memcached_conf = self.installMemcached(ip=self.getLocalIPv4Address(),
        port=11000)
    conversion_server_conf = self.installConversionServer(
        self.getLocalIPv4Address(), 23000, 23060)
    mysql_conf = self.installMysqlServer(self.getLocalIPv4Address(), 45678)
    user, password = self.installERP5()
    zodb_dir = os.path.join(self.data_root_directory, 'zodb')
    self._createDirectory(zodb_dir)
    ip = self.getLocalIPv4Address()
    mount_point_zeo_dict = self._getZeoClusterDict()
    zeo_conf = self.installZeo(ip)
    zodb_configuration_list = []
    known_tid_storage_identifier_dict = {}
    for mount_point, (storage_dict, check_path) in mount_point_zeo_dict.iteritems():
      known_tid_storage_identifier_dict[
        (((storage_dict['ip'],storage_dict['port']),), storage_dict['storage_name'])
        ] = (zeo_conf[storage_dict['storage_name']]['path'], check_path or mount_point)
      zodb_configuration_list.append(self.substituteTemplate(
        self.getTemplateFilename('zope-zeo-snippet.conf.in'), dict(
        storage_name=storage_dict['storage_name'],
        address='%s:%s' % (storage_dict['ip'], storage_dict['port']),
        mount_point=mount_point
        )))
    tidstorage_config = dict(host=self.getLocalIPv4Address(), port='6001')
    zodb_configuration_string = '\n'.join(zodb_configuration_list)
    zope_port = 12000
    # One Distribution Node
    zope_port += 1
    self.installZope(ip, zope_port, 'zope_distribution', with_timerservice=True,
        zodb_configuration_string=zodb_configuration_string,
        tidstorage_config=tidstorage_config)
    # Two Activity Nodes
    for i in (1, 2):
      zope_port += 1
      self.installZope(ip, zope_port, 'zope_activity_%s' % i,
          with_timerservice=True,
          zodb_configuration_string=zodb_configuration_string,
          tidstorage_config=tidstorage_config)
    # Four Web Page Nodes (Human access)
    login_url_list = []
    for i in (1, 2, 3, 4):
      zope_port += 1
      login_url_list.append(self.installZope(ip, zope_port,
        'zope_login_%s' % i, with_timerservice=False,
        zodb_configuration_string=zodb_configuration_string,
        tidstorage_config=tidstorage_config))
    backend_key, backend_certificate = self.requestCertificate(
        'Login Based Access')
    login_haproxy = self.installHaproxy(ip, 15001, 'login', self.site_check_path,
        login_url_list)
    apache_login = self.installBackendApache(self.getGlobalIPv6Address(), 15000,
        login_haproxy, backend_key, backend_certificate)
    apache_frontend_login = self.installFrontendZopeApache(
        self.getGlobalIPv6Address(), 4443, 'vifib', '/',
        apache_login, '/', backend_key, backend_certificate)
    # Four Web Service Nodes (Machine access)
    service_url_list = []
    for i in (1, 2, 3, 4):
      zope_port += 1
      service_url_list.append(self.installZope(ip, zope_port,
        'zope_service_%s' % i, with_timerservice=False,
        zodb_configuration_string=zodb_configuration_string,
        tidstorage_config=tidstorage_config))
    service_haproxy = self.installHaproxy(ip, 15000, 'service',
        self.site_check_path, service_url_list)

    key_auth_key, key_auth_certificate = self.requestCertificate(
        'Key Based Access')
    apache_keyauth = self.installKeyAuthorisationApache(
        self.getLocalIPv4Address(), 15500, service_haproxy, key_auth_key,
        key_auth_certificate, ca_conf, key_auth_path=self.key_auth_path)
    memcached_conf = self.installMemcached(ip=self.getLocalIPv4Address(),
        port=11000)
    kumo_conf = self.installKumo(self.getLocalIPv4Address())
    self.installTidStorage(tidstorage_config['host'], tidstorage_config['port'],
        known_tid_storage_identifier_dict, 'http://'+login_haproxy)
    self.linkBinary()

    # Connect direct to Zope to create the instance.
    self.installERP5Site(user, password, service_url_list[-1], mysql_conf,
             conversion_server_conf, memcached_conf, kumo_conf,
             self.site_id, self.default_bt5_list)

    self.setConnectionDict(dict(
      front_end_url=apache_frontend_login,
      site_url=apache_login,
      site_user=user,
      site_password=password,
      service_url=apache_keyauth,
      memcached_url=memcached_conf['memcached_url'],
      kumo_url=kumo_conf['kumo_address'],
      conversion_server_url='%(conversion_server_ip)s:%(conversion_server_port)s' %
        conversion_server_conf,
      # openssl binary might be removed, as soon as CP environment will be
      # fully controlled
      openssl_binary=self.options['openssl_binary'],
      # As soon as there would be Vifib ERP5 configuration and possibility to
      # call it over the network this can be removed
      certificate_authority_path=ca_conf['certificate_authority_path'],
      # as installERP5Site is not trusted (yet) and this recipe is production
      # ready expose more information
      mysql_url='%(mysql_database)s@%(ip)s:%(tcp_port)s %(mysql_user)s %(mysql_password)s' % mysql_conf,
    ))
    return self.path_list

  def installDevelopment(self):
    ca_conf = self.installCertificateAuthority()
    memcached_conf = self.installMemcached(ip=self.getLocalIPv4Address(),
        port=11000)
    conversion_server_conf = self.installConversionServer(
        self.getLocalIPv4Address(), 23000, 23060)
    mysql_conf = self.installMysqlServer(self.getLocalIPv4Address(), 45678)
    user, password = self.installERP5()
    zodb_dir = os.path.join(self.data_root_directory, 'zodb')
    self._createDirectory(zodb_dir)
    zodb_root_path = os.path.join(zodb_dir, 'root.fs')
    ip = self.getLocalIPv4Address()
    zope_port = '18080'
    zope_access = self.installZope(ip, zope_port, 'zope_development',
        zodb_configuration_string=self.substituteTemplate(
          self.getTemplateFilename('zope-zodb-snippet.conf.in'),
          dict(zodb_root_path=zodb_root_path)),
          thread_amount=8, with_timerservice=True)
    service_haproxy = self.installHaproxy(ip, 15000, 'service',
        self.site_check_path, [zope_access])
    key_auth_key, key_auth_certificate = self.requestCertificate(
        'Key Based Access')
    apache_keyauth = self.installKeyAuthorisationApache(
        self.getLocalIPv4Address(), 15500, service_haproxy, key_auth_key,
        key_auth_certificate, ca_conf, key_auth_path=self.key_auth_path)
    memcached_conf = self.installMemcached(ip=self.getLocalIPv4Address(),
        port=11000)
    kumo_conf = self.installKumo(self.getLocalIPv4Address())
    self.installTestRunner(ca_conf, mysql_conf, conversion_server_conf,
        memcached_conf, kumo_conf)
    self.installTestSuiteRunner(ca_conf, mysql_conf, conversion_server_conf,
                           memcached_conf, kumo_conf)
    self.linkBinary()
    self.installERP5Site(user, password, zope_access, mysql_conf,
             conversion_server_conf, memcached_conf, kumo_conf,
             self.site_id, self.default_bt5_list)

    self.setConnectionDict(dict(
      development_zope='http://%s:%s/' % (ip, zope_port),
      site_user=user,
      site_password=password,
      service_url=apache_keyauth,
      memcached_url=memcached_conf['memcached_url'],
      kumo_url=kumo_conf['kumo_address'],
      conversion_server_url='%(conversion_server_ip)s:%(conversion_server_port)s' %
        conversion_server_conf,
      # openssl binary might be removed, as soon as CP environment will be
      # fully controlled
      openssl_binary=self.options['openssl_binary'],
      # As soon as there would be Vifib ERP5 configuration and possibility to
      # call it over the network this can be removed
      certificate_authority_path=ca_conf['certificate_authority_path'],
      # as installERP5Site is not trusted (yet) and this recipe is production
      # ready expose more information
      mysql_url='%(mysql_database)s@%(ip)s:%(tcp_port)s %(mysql_user)s %(mysql_password)s' % mysql_conf,
    ))
    return self.path_list

  def _install(self):
    self.site_check_path = '/%s/getId' % self.site_id
    self.key_auth_path = '/%s/portal_slap' % self.site_id
    self.path_list = []
    self.requirements, self.ws = self.egg.working_set()
    # self.cron_d is a directory, where cron jobs can be registered
    self.cron_d = self.installCrond()
    self.logrotate_d, self.logrotate_backup = self.installLogrotate()
    self.killpidfromfile = zc.buildout.easy_install.scripts(
        [('killpidfromfile', 'slapos.recipe.erp5.killpidfromfile',
          'killpidfromfile')], self.ws, sys.executable, self.bin_directory)[0]
    self.path_list.append(self.killpidfromfile)
    if self.parameter_dict.get("flavour", "default") == 'configurator':
      self.default_bt5_list = self.options.get("configurator_bt5_list", '').split()

    if self.parameter_dict.get('development', 'false').lower() == 'true':
      return self.installDevelopment()
    if self.parameter_dict.get('production', 'false').lower() == 'true':
      return self.installProduction()
    raise NotImplementedError('Flavour of instance have to be given.')