#!/usr/bin/python
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2012 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 advised 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 ConfigParser
import logging
from optparse import OptionParser, Option
import os
import sys
import tempfile
import urllib2


# create console handler and set level to debug
ch = logging.StreamHandler()
ch.setLevel(logging.WARNING)
# create formatter
formatter = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
# add formatter to ch
ch.setFormatter(formatter)



class Parser(OptionParser):
  """
  Parse all arguments.
  """
  def __init__(self, usage=None, version=None):
    """
    Initialize all options possibles.
    """
    OptionParser.__init__(self, usage=usage, version=version,
                          option_list=[
      Option("--slapos-configuration",
             help="path to slapos configuration directory",
             default='/etc/opt/slapos/',
             type=str),
      Option("--slapos-cron",
             help="path to slapos cron file",
             default='/etc/cron.d/slapos-node',
             type=str),
      Option("--check-upload",
             help="Check if upload parameters are ok (do not check certificates)",
             default=False,
             action="store_true"),
      Option("--test-agent",
             help="Check if parameters are good for a test agent",
             default=False,
             action="store_true"),
      Option("-v","--verbose",
             default=False,
             action="store_true",
             help="Verbose output."),
      Option("-n", "--dry-run",
             help="Simulate the execution steps",
             default=False,
             action="store_true"),
   ])

  def check_args(self):
    """
    Check arguments
    """
    (options, args) = self.parse_args()
    if options.test_agent :
      options.check_upload = True
    return options


def get_slapos_conf_example():
  """
  Get slapos.cfg.example and return its path
  """
  register_server_url = "http://git.erp5.org/gitweb/slapos.core.git/blob_plain/HEAD:/slapos.cfg.example"
  request = urllib2.Request(register_server_url)
  url = urllib2.urlopen(request)  
  page = url.read()
  info, path = tempfile.mkstemp()
  slapos_cfg_example = open(path,'w')
  slapos_cfg_example.write(page)
  slapos_cfg_example.close()
  return path

  
def check_networkcache(config,logger,configuration_parser):
  """
  Check network cache download
  """
  slapos_cfg_example = get_slapos_conf_example()
  configuration_example_parser = ConfigParser.RawConfigParser()
  configuration_example_parser.read(slapos_cfg_example)  
  os.remove(slapos_cfg_example)
  section = "networkcache"
  configuration_example_dict = dict(configuration_example_parser.items(section))
  configuration_dict = dict(configuration_parser.items(section))
  for key in configuration_example_dict:
    try:
      if not configuration_dict[key] ==  configuration_example_dict[key] :
        logger.warn("%s parameter in %s section is out of date" % (key, section))
    except KeyError:
      logger.warn("No %s parameter in your file" % key)
      pass
  
  if config.test_agent:
    configuration_dict = dict(configuration_parser.items('slapformat'))
    if int(configuration_dict['partition_amount']) < 60 :
      logger.warn("Partition amount is to low for a test agent. Is %s but should be at least 60"
                  % configuration_dict['partition_amount'] )

  if config.check_upload == True :
    check_networkcache_upload(config,logger,configuration_dict)

class Upload:
  """
  Class used as a reference to check network cache upload 
  """
  def __init__(self):
    self.data = {'download-binary-dir-url': 'http://www.shacache.org/shadir',
                 'signature_certificate_file': '/etc/slapos-cache/signature.cert',
                 'upload-dir-url': 'https://www.shacache.org/shadir',
                 'shadir-cert-file': '/etc/slapos-cache/shacache.cert',
                 'download-cache-url': 'https://www.shacache.org/shacache',
                 'upload-cache-url': 'https://www.shacache.org/shacache', 
                 'shacache-cert-file': '/etc/slapos-cache/shacache.cert', 
                 'upload-binary-cache-url': 'https://www.shacache.org/shacache', 
                 'shacache-key-file': '/etc/slapos-cache/shacache.key', 
                 'download-binary-cache-url': 'http://www.shacache.org/shacache',
                 'upload-binary-dir-url':'https://www.shacache.org/shadir', 
                 'signature_private_key_file': '/etc/slapos-cache/signature.key', 
                 'shadir-key-file': '/etc/slapos-cache/shacache.key'}


def check_networkcache_upload(config,logger,configuration_dict):
  """
  Check network cache upload
  """
  upload_parameters = Upload()
  for key in upload_parameters.data:
    try:
      if not key.find("file") == -1:
        file = configuration_dict[key]
        if not os.path.exists(file) :
          logger.critical ("%s file for %s parameters does not exist " 
                         % (file,key)) 
        else :
          logger.info ("%s parameter:%s does exists" % (key,file))
      else :
        if not configuration_dict[key] == upload_parameters.data[key]:
          logger.warn("%s is %s sould be %s" 
                      %(key,configuration_dict[key]
                        ,upload_parameters.data[key]))
    except KeyError:
      logger.critical ("No %s parameter in your file" % key)
      pass


def slapos_conf_check (config):
  """
  Check if slapos.cfg look good
  """
  # Define logger for slapos.cfg verification
  logger = logging.getLogger('Checking slapos.cfg file:')
  logger.setLevel(logging.INFO)
  logger.addHandler(ch)
  # Load configuration file
  configuration_file_path = os.path.join (config.slapos_configuration
                                          ,'slapos.cfg')
  configuration_parser = ConfigParser.SafeConfigParser()
  configuration_parser.read(configuration_file_path)
  # Check if files for slapos and slapformat exists
  for section in ("slapformat", "slapos"):
    configuration_dict = dict(configuration_parser.items(section))
    for key in configuration_dict:
      if key in ("key_file","cert_file","certificate_repository_path"):
        files = configuration_dict[key]
        if not os.path.exists(files) :
          logger.critical ("%s file for %s parameters does not exist " 
                           % (files,key)) 
        else :
          logger.info ("%s parameter:%s does exists" % (key,files))
  # Check networkcache
  check_networkcache(config,logger,configuration_parser)


class CronLine:
  """
  Class to analyse each cron line individualy
  """
  def __init__(self):
    """ Init all value to None"""
    self.command = None
    self.pidfile = None
    self.logfile = None
    self.config = None

  def parse(self,cron_line):
    """ Parse cron line and give value to attributes """
    line = cron_line.split()
    self.command = line[6]
    for word in line:
      if "slapos.cfg" in word :
        self.config = word
      if "--pidfile" in word :
        self.pidfile = word[word.find("=")+1:]
      if "--log_file" in word and "format" in self.command:
        self.logfile = word[word.find("=")+1:]
      if "--logfile" in word and not "format" in self.command:
        self.logfile = word[word.find("=")+1:]

  def check(self,config,logger):
    """ Check if all attributes are correctly set"""
    if not os.path.exists(self.config): 
      logger.critical("For %s command: slapos.cfg is %s should be in  %s" 
                      % (self.command,self.config,config.slapos_configuration))      
    elif not os.path.samefile(self.config,os.path.join(config.slapos_configuration,'slapos.cfg')):
      logger.critical("For %s command: slapos.cfg is %s should be in  %s" 
                      % (self.command,self.config,config.slapos_configuration))            
    if self.pidfile == None:
      logger.warning("For %s command: No pidfile" 
                       % (self.command))
    if self.logfile == None:
      logger.warning("For %s command: No logfile" 
                       % (self.command))


def cron_check (config):
  """
  Check cron file
  """
  # Define logger for cron file verification
  logger = logging.getLogger('Checking slapos-node cron file:')
  logger.setLevel(logging.INFO)
  logger.addHandler(ch)
  cron = open(config.slapos_cron,"r")  
  for line in cron :
    if "/opt/slapos" in line and not line[0]=="#":
      cron_line = CronLine()
      cron_line.parse(line)
      cron_line.check(config,logger)

def slapos_global_check (config):
  """
  Check for main files
  """
  # Define logger for computer chek
  logger = logging.getLogger('Checking your computer for SlapOS:')
  logger.setLevel(logging.INFO)
  logger.addHandler(ch)
  # checking slapos.cfg
  if not os.path.exists(os.path.join(config.slapos_configuration,'slapos.cfg')) :
    logger.critical("No slapos.cfg found in slapos configuration directory: %s" 
                    % config.slapos_configuration )
  else :
    logger.info("SlapOS configuration file found")
    slapos_conf_check(config)
  # checking cron file
  if not os.path.exists(config.slapos_cron) :
    logger.warn("No %s found for cron" % config.slapos_cron)
  else:
    logger.info("Cron file found at %s" %config.slapos_cron)
    cron_check(config)

# Class containing all parameters needed for configuration
class Config:
  def setConfig(self, option_dict):
    """
    Set options given by parameters.
    """
    # Set options parameters
    for option, value in option_dict.__dict__.items():
      setattr(self, option, value)
    # Define logger for register
    self.logger = logging.getLogger('slaptest configuration')
    self.logger.setLevel(logging.DEBUG)
    # add ch to logger
    self.logger.addHandler(ch)

    if self.verbose :
      ch.setLevel(logging.DEBUG)


  def displayUserConfig(self):
    self.logger.debug ("Slapos.cfg : %s" % self.slapos_configuration)
    self.logger.debug ("slapos cron file: %s" % self.slapos_cron)


def main():
  """Checking computer state to run slapos"""
  usage = "usage: %s [options] " % sys.argv[0]
  # Parse arguments
  config = Config()
  config.setConfig(Parser(usage=usage).check_args())
  config.displayUserConfig()
  slapos_global_check(config)
  sys.exit()


if __name__ == "__main__":
  main()