#!/usr/bin/env python

# Copyright (C) 2009-2012:
#    Gabes Jean, naparuba@gmail.com
#    Gerhard Lausser, Gerhard.Lausser@consol.de
#    Gregory Starck, g.starck@gmail.com
#    Hartmut Goebel, h.goebel@goebel-consult.de
#
# This file is part of Shinken.
#
# Shinken is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Shinken 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Shinken.  If not, see <http://www.gnu.org/licenses/>.

"""

Warning: THIS IS A PREVIEW, and uglu code that looks as a script.

"""

import optparse
import sys
import os
import re
import tempfile
import json
import shutil
import zipfile
import pycurl
from StringIO import StringIO

try:
    import shinken
    from shinken.bin import VERSION
except ImportError:
    # If importing shinken fails, try to load from current directory
    # or parent directory to support running without installation.
    # Submodules will then be loaded from there, too.
    import imp
    imp.load_module('shinken', *imp.find_module('shinken', [os.path.realpath("."), os.path.realpath(".."), os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), "..")]))
    from shinken.bin import VERSION

from shinken.log import logger
from shinken.objects.config import Config

logger.set_level(logger.DEBUG)


class Dummy():
    def __init__(self): pass

    def add(self, obj): pass
logger.load_obj(Dummy())

if os.name != 'nt':
    DEFAULT_CFG = '/etc/shinken/skonf.cfg'
else:
    DEFAULT_CFG = 'c:\\shinken\\etc\\skonf.cfg'


def create_pack(pack_file, p_dest_dir, share_dir):
    _tmp = tempfile.mktemp()

    print "Using the destination directory: %s" % p_dest_dir
    src_dir = os.path.abspath(os.path.dirname(pack_file))
    print "Source dir that will be load", src_dir

    print "Will use the source temp file %s" % _tmp
    f = open(_tmp, 'w')
    f.write('cfg_dir=%s\n' % src_dir)
    f.flush()
    print "Saved tmp file"

    cfg = Config()
    buf = cfg.read_config([_tmp])
    raw_objects = cfg.read_config_buf(buf)
    cfg.create_objects(raw_objects)

    # Now we can close the cfg temp file
    f.close()

    print "Loading pack files"
    cfg.load_packs()

    print "Packs", cfg.packs

    packs = [p for p in cfg.packs]
    if len(packs) > 1:
        logger.log("Sorry, there is too much .pack file in the directory %s, please get only one .pack for this directory and it's subdirectories")
        sys.exit(2)

    o = {}
    pack = packs.pop()
    o['name'] = pack.get_name()
    o['description'] = pack.description
    o['templates'] = pack.templates
    o['services'] = {}
    o['commands'] = []
    print "We are creating the pack '%s'" % o['name']
    print "Description: %s" % o['description']
    print "Host templates: %s" % ','.join(o['templates'])
    # We will dump the services linked to theses templates
    for h in cfg.hosts:
        if h.is_tpl() and hasattr(h, 'name') and not h.name in o['templates']:
            print "WARNING: you miss the host template '%s' in the pack file. I'm adding it" % h.name
            o['templates'].append(h.name)

    for s in cfg.services:
        if s.is_tpl() and hasattr(s, 'service_description') and hasattr(s, 'host_name'):
            desc = s.service_description
            if not desc in o['services']:
                o['services'][desc] = {'templates': []}
            tpls = s.host_name.split(',')
            tpls = [t.strip() for t in tpls]
            for t in tpls:
                o['services'][desc]['templates'].append(t)
            # Make elements uniq
            o['services'][desc]['templates'] = list(set(o['services'][desc]['templates']))

    for c in cfg.commands:
        cname = getattr(c, 'command_name', '')
        if cname in ['bp_rule', '_internal_host_up']:
            continue
        if cname:
            o['commands'].append(cname)
    # Make comands uniq
    o['commands'] = list(set(o['commands']))

    o['path'] = pack.path
    print "Path: %s" % o['path']
    o['doc_link'] = pack.doc_link
    print "Doc link: %s" % o['doc_link']
    o['macros'] = {}
    print "Macros:"
    for (k, v) in pack.macros.iteritems():
        t = v.get('type', 'string')
        d = v.get('description', '')
        o['macros'][k] = {'type': t, 'description': ''}
        print "\t%s: Type=%s Description='%s'" % (k, t, d)

    j = json.dumps(o, sort_keys=True, indent=3)
    print "Pack file generated:", j

    create_dir = os.path.join(p_dest_dir, o['name'])
    print "We will create the pack directory in %s" % create_dir
    # Now get all the .cfg files from the src_dir and put them into a pack directory
    if os.path.exists(create_dir) and os.path.isdir(create_dir):
        print "Warning: the destination directory already exists. I clean it!"
        #TODO: do the clean
        shutil.rmtree(create_dir)

    os.mkdir(create_dir)
    pack_file_dest = os.path.join(create_dir, o['name'] + '.pack')
    print "Saving the .pack file into %s" % pack_file_dest
    f = open(pack_file_dest, 'w')
    f.write(j)
    f.close()

    for root, dirs, files in os.walk(src_dir):
        for file in files:
            if re.search("\.cfg$", file):
                base = root[len(src_dir):].strip()
                if base.startswith('/'):
                    base = base[1:]
                to_save = os.path.join(root, file)
                print "We will save the file", base, to_save
                dest_dir = os.path.join(create_dir, base)
                print "Save in the directory", dest_dir
                dest_file = os.path.join(dest_dir, file)
                print "Into the file", dest_file
                if not os.path.exists(dest_dir):
                    os.mkdir(dest_dir)
                shutil.copy(to_save, dest_file)

    # Create the shares dir
    #os.mkdir(os.path.join(create_dir, 'share'))
    os.mkdir(os.path.join(create_dir, 'images'))
    os.mkdir(os.path.join(create_dir, 'images', 'sets'))
    os.mkdir(os.path.join(create_dir, 'templates'))
    os.mkdir(os.path.join(create_dir, 'templates', 'graphite'))
    os.mkdir(os.path.join(create_dir, 'templates', 'pnp'))

    # Now look for the share dir and for push icon sets or
    if share_dir:
        print "We will parse the share tree to find related files"
        sets_dir = os.path.join(share_dir, 'images', 'sets')
        if os.path.exists(sets_dir):
            print "Looking the share/images/sets"
            # We are looking for the hosttags and the template
            # name too
            _img_names = pack.templates
            _img_names.append(o['name'])
            # Ok but uniq please :)
            _img_names = list(set(_img_names))
            for t in _img_names:
                print "Lookng for the template", t
                _p = os.path.join(sets_dir, t)
                if os.path.exists(_p) and os.path.isdir(_p):
                    _d = os.path.join(create_dir, 'images', 'sets', t)
                    #if not os.path.exists(_d):
                    #    os.mkdir(_d)
                    print "COPY %s into %s" % (_p, _d)
                    shutil.copytree(_p, _d)

        graphite_templates_dir = os.path.join(share_dir, 'templates', 'graphite')
        if os.path.exists(graphite_templates_dir):
            print "Looking the share/templates/graphite dir"
            for c in cfg.commands:
                cname = c.get_name()
                if cname in ['bp_rule', '_internal_host_up']:
                    continue
                print "LOOK FOR command", c.get_name()
                _p = os.path.join(graphite_templates_dir, cname + '.graph')
                _d = os.path.join(create_dir, 'templates', 'graphite', cname + '.graph')
                print "So search for graphite template", _p
                if os.path.exists(_p):
                    print "COPYING %s into %s" % (_p, _d)
                    shutil.copy(_p, _d)

        pnp_templates_dir = os.path.join(share_dir, 'templates', 'pnp')
        if os.path.exists(pnp_templates_dir):
            print "Looking the share/templates/pnp dir"
            for c in cfg.commands:
                cname = c.get_name()
                if cname in ['bp_rule', '_internal_host_up']:
                    continue
                print "LOOK FOR command", c.get_name()
                _p = os.path.join(graphite_templates_dir, cname + '.php')
                _d = os.path.join(create_dir, 'templates', 'pnp', cname + '.php')
                print "So search for graphite template", _p
                if os.path.exists(_p):
                    print "COPYING %s into %s" % (_p, _d)
                    shutil.copy(_p, _d)

    zip_file_p = os.path.join(p_dest_dir, o['name'] + '.zip')
    print "Want to save the zip file", zip_file_p

    fd = zipfile.ZipFile(zip_file_p, "w")

    for root, dirs, files in os.walk(create_dir):
        for file in files:
            name = os.path.join(root, file)
            dst_name = name[len(create_dir):]
            if dst_name.startswith('/'):
                dst_name = dst_name[1:]
            print "Saving in zip", name, 'as', dst_name
            fd.write(name, dst_name, zipfile.ZIP_DEFLATED)
    fd.close()

    print "The pakc file is saved as", zip_file_p


def push_pack(zip_file_p, api_key, uri, proxy):
    # open the file again, to see what's in it
    #file = zipfile.ZipFile(zip_file_p, "r")
    #for info in file.infolist():
    #    print info.filename

    print "GO PUSH"
    print "LAUNCH THE FILE", zip_file_p

    import pycurl

    c = pycurl.Curl()
    c.setopt(c.POST, 1)
    c.setopt(c.CONNECTTIMEOUT, 5)
    c.setopt(c.TIMEOUT, 8)
    if proxy:
        c.setopt(c.PROXY, proxy)
    c.setopt(c.URL, uri + "/pushpack")

    c.setopt(c.HTTPPOST, [("key", api_key),
                          ("data",
                           (c.FORM_FILE, str(zip_file_p),
                            c.FORM_CONTENTTYPE, "application/x-gzip"))
                          ])

    #c.setopt(c.HTTPPOST, [("file1", (c.FORM_FILE, str(zip_file_p)))])
    #c.setopt(c.VERBOSE, 1)
    c.perform()
    print "status code: %s" % c.getinfo(pycurl.HTTP_CODE)
    c.close()
    print "Thanks :) "


def register(login, password, email, uri, proxy):
    print "GO register"
    #import os, urllib
    #os.environ["http_proxy"] = "http://proxy:3128"
    #data = urllib.urlopen("http://www.google.fr").read()
    #print "DATA?", data

    print "Trying to register the user", login, "with the email", email

    c = pycurl.Curl()
    c.setopt(c.POST, 1)
    c.setopt(c.CONNECTTIMEOUT, 5)
    c.setopt(c.TIMEOUT, 8)
    if proxy:
        c.setopt(c.PROXY, proxy)
    c.setopt(c.URL, uri + "/register")

    c.setopt(c.HTTPPOST, [("username", login), ("password", password), ("email", email), ('cli_mode', '1')])

    error = ''
    obj = ''
    #c.setopt(c.VERBOSE, 1)
    response = StringIO()
    c.setopt(c.WRITEFUNCTION, response.write)
    c.perform()
    response.seek(0)
    status_code = c.getinfo(pycurl.HTTP_CODE)
    # We only parse the json if we got
    if status_code == 200:
        obj = json.loads(response.read().replace('\\/', '/'))
    else:
        error = response.read().replace('\\/', '/')

    c.close()
    print "status code: %s" % status_code
    if error:
        print "Sorry there was an error with the query:", error
        sys.exit(2)
    state = obj['state']
    text = obj['text']
    if state == 200:
        print "OK:", text
        sys.exit(0)
    else:
        print "Error:", text
        sys.exit(2)


def getkey(login, password, uri, proxy):
    print "GO get your key"
    #import os, urllib
    #os.environ["http_proxy"] = "http://proxy:3128"
    #data = urllib.urlopen("http://www.google.fr").read()
    #print "DATA?", data

    print "Trying to get your API KEY for", login

    c = pycurl.Curl()
    c.setopt(c.POST, 1)
    c.setopt(c.CONNECTTIMEOUT, 5)
    c.setopt(c.TIMEOUT, 10)
    if proxy:
        c.setopt(c.PROXY, proxy)
    c.setopt(c.URL, uri + "/apikey")

    c.setopt(c.HTTPPOST, [("login", login), ("password", password), ('cli_mode', '1')])

    #c.setopt(c.HTTPPOST, [("file1", (c.FORM_FILE, str(zip_file_p)))])
    error = ''
    #c.setopt(c.VERBOSE, 1)
    response = StringIO()
    c.setopt(c.WRITEFUNCTION, response.write)
    c.perform()
    response.seek(0)
    status_code = c.getinfo(pycurl.HTTP_CODE)
    # We only parse the json if we got
    if status_code == 200:
        obj = json.loads(response.read().replace('\\/', '/'))
    else:
        error = response.read().replace('\\/', '/')

    c.close()
    print "status code: %s" % status_code
    if error:
        print "Sorry there was an error with the query:", error
        sys.exit(2)

    print "You api key is", obj['api_key']


def analyse(api_key, stats_file, uri, proxy):
    if not api_key:
        print "Error: Sorry, analysing the configuration need an API key"
        sys.exit(2)
    if not stats_file:
        print "Error: missing statitics files for analysing"
        sys.exit(2)

    import pycurl

    c = pycurl.Curl()
    c.setopt(c.POST, 1)
    c.setopt(c.CONNECTTIMEOUT, 5)
    c.setopt(c.TIMEOUT, 8)
    if proxy:
        c.setopt(c.PROXY, proxy)
    c.setopt(c.URL, uri + "/pushstats")

    c.setopt(c.HTTPPOST, [("key", api_key),
                          ("data",
                           (c.FORM_FILE, str(stats_file),
                            c.FORM_CONTENTTYPE, "application/x-gzip"))
                          ])

    #c.setopt(c.HTTPPOST, [("file1", (c.FORM_FILE, str(zip_file_p)))])
    c.setopt(c.VERBOSE, 1)
    c.perform()
    print "status code: %s" % c.getinfo(pycurl.HTTP_CODE)
    c.close()
    print "that's it ;)"





if __name__ == '__main__':
    parser = optparse.OptionParser(
        """
Create   : %prog [options] -c -p pack_file [-s share_dir] [-d dest]
Upload   : %prog [options] -u -z pack.zip -k API_KEY
Register : %prog [options] -r -l login -P password -e email
Retrieve : %prog [options] -g -l login -P password
Analyse  : %prog [options] -a -k API_KEY /path/stats.json

For more information, look at http://www.shinken-monitoring.org/wiki/packs/create_and_push_packs
""",
        version="%prog " + VERSION)
    parser.add_option('-c', '--create', action='store_true',
                      dest="create_pack", help=('Mode to create a zip file from a pack'))
    parser.add_option('-p', '--pack-file', dest="pack_file",
                      help="""Pack .pack file that you want to generate from.""")
    parser.add_option('-s', '--share-dir', dest="share_dir",
                       help=('Your share directory with templates and icon sets'))
    parser.add_option('-d', '--dest', dest="p_dest_dir",
                      help="""Directory where to """)
    parser.add_option('-U', '--uri', dest="uri",
                      help="""Uri to push the pack. Default is community.shinken-monitoring.org""")
    parser.add_option('--proxy', dest="proxy",
                      help="""Proxy URI. Like http://user:password@proxy-server:3128""")

    parser.add_option('-u', '--upload', action='store_true',
                      dest="upload", help=('Mode to upload a pack zip file to the Shinken Pack website. It need an API key for it.'))
    parser.add_option('-z', '--zip-file',
                      dest="zip_file", help=("Your Pack zip file to send in the Shinken Pack website"))
    # TODO: where is the PACK WEBSITE??? :)
    parser.add_option('-k', '--api-key',
                      dest="api_key", help=("Your API key for uploading the pack zip file in the Shinken Pack website. If you don't have one, please go to XXXXXX"))

    parser.add_option('-r', '--register', action='store_true',
                      dest="register", help=("Mode to register to the Shinken pack website"))
    parser.add_option('-l', '--login',
                      dest="login", help=("Login name to the Shinken Pack website, to register or get the API key"))
    parser.add_option('-P', '--password',
                      dest="password", help=("Password for the Shinken pack website registration"))
    parser.add_option('-e', '--email',
                      dest="email", help=("Email for the Shinken pack website registration. Must be valid."))

    parser.add_option('-g', '--get-key', action='store_true',
                      dest="getkey", help=("Mode to retrieve your API key"))

    parser.add_option('-a', '--analyse', action='store_true',
                      dest="analyse", help=("Mode to analyse your configuration and upload it's characteristics to the server. Only statistics data, no hostnames, usernames not ips will be sent"))

    opts, args = parser.parse_args()

    mode = ''

    # For the create pack part
    pack_file = ''
    p_dest_dir = tempfile.gettempdir()
    src_dir = ''
    _tmp = tempfile.mktemp()
    share_dir = opts.share_dir or ''

    uri = opts.uri or 'community.shinken-monitoring.org'
    proxy = opts.proxy or None

    if opts.create_pack:
        mode = 'create_pack'

        if not opts.pack_file:
            parser.error("Requires a pack file (option -p/--pack-file)")
        pack_file = opts.pack_file

        if not pack_file.endswith('.pack'):
            print "Sorry, the pack-file is not a .pack file. exiting"
            sys.exit(2)

        if opts.p_dest_dir:
            p_dest_dir = opts.p_dest_dir

        create_pack(pack_file, p_dest_dir, share_dir)



    if opts.upload:
        mode = 'upload'
        if not opts.zip_file:
            parser.error("Requires a pack zip file (option -z/--zip-file)")
        zip_file = opts.zip_file

        if not opts.api_key:
            parser.error("Requires an API key (option -k/--api-key)")
        api_key = opts.api_key

        push_pack(zip_file, api_key, uri, proxy)


    if opts.register:
        mode = 'register'
        if not opts.login:
            parser.error("Requires a login name (option -l/--login)")
        login = opts.login
        if not opts.password:
            parser.error("Requires a password (option -P/--password)")
        password = opts.password
        if not opts.email:
            parser.error("Requires a valid email (option -e/--email)")
        email = opts.email

        register(login, password, email, uri, proxy)


    if opts.getkey:
        mode = 'getkey'
        if not opts.login:
            parser.error("Requires a login name (option -l/--login)")
        login = opts.login
        if not opts.password:
            parser.error("Requires a password (option -P/--password)")
        password = opts.password

        getkey(login, password, uri, proxy)



    if opts.analyse:
        mode = 'analyse'
        if not opts.api_key:
            parser.error("Requires an API key (option -k/--api-key)")
        api_key = opts.api_key
        files = args
        if not files:
            parser.error("Requires a statistic file to send")
        if len(files) != 1:
            parser.error("Too much statistics files to send")
        p = files[0]
        print "Sending statistics in file ", p
        analyse(api_key, p, uri, proxy)
