# SDPEnv.py
#------------------------------------------------------------------------------
# Copyright (c) Perforce Software, Inc., 2007-2014. All rights reserved
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1  Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#
# 2.  Redistributions in binary form must reproduce the above copyright
#     notice, this list of conditions and the following disclaimer in the
#     documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PERFORCE
# SOFTWARE, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
# DAMAGE.
#------------------------------------------------------------------------------
# Utilities for creating or validating an environment based on a master configuration file

from __future__ import print_function

import os
import os.path
import sys
import subprocess
import socket
import shutil
import glob
import re
import textwrap
import logging
import argparse
from collections import OrderedDict

# Python 2.7/3.3 compatibility.
def python3():
    return sys.version_info[0] >= 3
    
if python3():
    from configparser import ConfigParser
    from io import StringIO
else:
    from ConfigParser import ConfigParser
    from StringIO import StringIO

import P4

MODULE_NAME = 'SDPEnv'
DEFAULT_CFG_FILE = 'sdp_master_config.ini'
DEFAULT_LOG_FILE = '%s.log' % MODULE_NAME
DEFAULT_VERBOSITY = 'INFO'

LOGGER_NAME = '%s.py' % MODULE_NAME
DEFAULTS_SECTION = 'Defaults'

# Standard configure lines for an instance - these are arguments to
# p4 configure set <values below>
STANDARD_CONFIGURE_LINES = """db.peeking=2
    dm.user.noautocreate=2
    dm.user.resetpassword=1
    filesys.P4ROOT.min=1G
    filesys.depot.min=1G
    filesys.P4JOURNAL.min=1G
    spec.hashbuckets=99
    monitor=1
    server=3
    net.tcpsize=128k
    server.commandlimits=2""".split("\n")

class SDPException(Exception):
    "Base exceptions"
    pass
class SDPConfigException(SDPException):
    "Exceptions in config"
    pass

def joinpath(root, *path_elts):
    "Deals with drive roots or a full path as the root"
    if len(root) > 0 and root[-1] == ":":
        return os.path.join(root, os.sep, *path_elts)
    else:
        return os.path.join(root, *path_elts)

def running_as_administrator():
    "Makes sure current process has admin rights"
    output = ""
    try:
        output = subprocess.check_output("net session", universal_newlines=True, 
                                    stderr=subprocess.STDOUT, shell=True)
    except subprocess.CalledProcessError as e:
        output = e.output
    return not re.search(r"Access is denied", output)

def find_record(rec_list, key_field, search_key):
    "Returns dictionary found in list of them"
    for rec in rec_list:
        if rec[key_field].lower() == search_key.lower():
            return rec
    return None

class SDPInstance(object):
    "A single instance"

    def __init__(self, config, section):
        "Expects a configparser"
        self._attrs = {}
        def_attrs = "p4name sdp_service_type sdp_hostname sdp_instance sdp_p4port_number sdp_os_username".split()
        def_attrs.extend("metadata_root depotdata_root logdata_root".split())
        for def_attr in def_attrs:
            self._attrs[def_attr] = ""
        for item in config.items(section):
            self._attrs[item[0]] = item[1]
        self._init_dirs()

    def __iter__(self):
        for attr in self._attrs.keys():
                    yield self._attrs[attr]

    def _init_dirs(self):
        "Initialises directory names for this instance"
        curr_scriptdir = os.path.dirname(os.path.realpath(__file__))
        self._attrs["sdp_common_bin_dir"] = os.path.abspath(os.path.join(curr_scriptdir, '..', 'p4', 'common', 'bin'))
        self._attrs["depotdata_dir"] = joinpath(self.depotdata_root, 'p4', self.sdp_instance)
        self._attrs["metadata_dir"] = joinpath(self.metadata_root, 'p4', self.sdp_instance)
        self._attrs["logdata_dir"] = joinpath(self.logdata_root, 'p4', self.sdp_instance)
        self._attrs["bin_dir"] = joinpath(self.depotdata_root, 'p4', self.sdp_instance, 'bin')
        self._attrs["common_bin_dir"] = joinpath(self.depotdata_root, 'p4', 'common', 'bin')
        self._attrs["root_dir"] = joinpath(self.metadata_root, 'p4', self.sdp_instance, 'root')
        self._attrs["logs_dir"] = joinpath(self.logdata_root, 'p4', self.sdp_instance, 'logs')
        self._attrs["sdp_config_dir"] = joinpath(self.depotdata_root, 'p4', 'config')


    def __getitem__(self, name):
        return object.__getattribute__(self, '_attrs').get(name)

    def is_current_host(self):
        "Checks against current hostname"
        return socket.gethostname().lower() == self._attrs["sdp_hostname"].lower()

    def __getattribute__(self, name):
        "Only allow appropriate attributes"
        if name in ["_attrs", "_init_dirs", "get", "is_current_host"]:
            return object.__getattribute__(self, name)
        else:
            if not name in object.__getattribute__(self, '_attrs'):
                raise AttributeError("Unknown attribute '%s'" % name)
            return object.__getattribute__(self, '_attrs').get(name, "")


class SDPConfig(object):
    "The main class to process SDP configurations"

    def __init__(self, config_data=None):
        self.config = None
        self.instances = {}
        self.commands = []  # List of command files to run - and their order
        parser = argparse.ArgumentParser(
            formatter_class=argparse.RawDescriptionHelpFormatter,
            description=textwrap.dedent('''\
            NAME

                SDPEnv.py

            VERSION

                1.0.0

            DESCRIPTION

                Create the environment for an SDP (Server Deployment Package)

            EXAMPLES

                SDPEnv.py -h

            EXIT CODES

                Zero indicates normal completion.  Non-zero indicates an error.

            '''),
            epilog="Copyright (c) 2008-2014 Perforce Software, Inc.  "
                "Provided for use as defined in the Perforce Consulting Services Agreement."
        )
        parser.add_argument('-y', '--yes', action='store_true',
                            help="Perform actual changes such as directory creation and file copying.")
        parser.add_argument('-c', '--config_filename', default=DEFAULT_CFG_FILE,
                            help="Config file, relative or absolute path. Default: " + DEFAULT_CFG_FILE)
        parser.add_argument('-r', '--report', action='store_true',
                            help="Report on current configuration")
        parser.add_argument('-L', '--log', default=DEFAULT_LOG_FILE,
                            help="Default: " + DEFAULT_LOG_FILE)
        parser.add_argument('-v', '--verbosity',
                            nargs='?',
                            const="INFO",
                            default=DEFAULT_VERBOSITY,
                            choices=('DEBUG', 'WARNING', 'INFO', 'ERROR', 'FATAL'),
                            help="Output verbosity level. Default is: " + DEFAULT_VERBOSITY)
        self.options = parser.parse_args()
        self.logger = logging.getLogger(LOGGER_NAME)
        self.logger.setLevel(self.options.verbosity)
        h = logging.StreamHandler()
        # df = datestamp formatter; bf= basic formatter.
        # df = logging.Formatter('%(asctime)s %(levelname)s: %(message)s', datefmt='%Y/%m/%d %H:%M:%S')
        bf = logging.Formatter('%(levelname)s: %(message)s')
        h.setFormatter(bf)
        self.logger.addHandler(h)
        self.logger.debug("Command Line Options: %s\n" % self.options)
        if not self.options.yes:
            self.logger.info("Running in reporting mode: use -y or --yes to perform actions.")
        self._read_config(self.options.config_filename, config_data)

    def _read_config(self, config_filename, config_data):
        "Read the configuration file"
        if config_data:  # testing
            config_file = StringIO(config_data)
        else:
            if not os.path.exists(config_filename):
                raise SDPException("Master config file not found: '%s'" % config_filename)
            config_file = open(config_filename)
        self.config = ConfigParser()
        self.config.readfp(config_file)
        self.logger.info("Found the following sections: %s" % self.config.sections())
        for section in self.config.sections():
            self.instances[section] = SDPInstance(self.config, section)

    def isvalid_config(self):
        "Check configuration read is valid"
        required_options = "p4name sdp_service_type sdp_hostname sdp_instance sdp_p4port_number sdp_os_username".split()
        required_options.extend("metadata_root depotdata_root logdata_root".split())
        errors = []
        for instance_name in self.instances.keys():
            missing = []
            fields = {}
            instance = self.instances[instance_name]
            # Check for require fields
            for opt in required_options:
                if instance[opt] == "":
                    missing.append(opt)
                else:
                    fields[opt] = instance[opt]
            if missing:
                errors.append("The following required options are missing '%s' in instance '%s'" % (
                        ", ".join(missing), instance_name))
            # Check for numeric fields
            field_name = "sdp_p4port_number"
            if field_name in fields:
                if not instance[field_name].isdigit():
                    errors.append("%s must be numeric in instance '%s'" % (field_name.upper(), instance_name))
            # Check for restricted values
            field_name = "sdp_service_type"
            if field_name in fields:
                valid_service_types = "standard replica forwarding-replica build-server".split()
                if not instance[field_name] in valid_service_types:
                    errors.append("%s must be one of '%s' in instance '%s'" % (field_name.upper(),
                                ", ".join(valid_service_types), instance_name))
        if errors:
            raise SDPConfigException("\n".join(errors))
        return True

    def get_master_instance_name(self):
        "Assumes valid config"
        #TODO - this assumes only one standard section
        for instance_name in self.instances:
            if self.instances[instance_name]["sdp_service_type"] == "standard":
                return instance_name
        raise SDPConfigException("No master section found")

    def write_master_config_ini(self):
        "Write the appropriate configure values"
        common_settings = """p4name sdp_os_username sdp_p4serviceuser
                metadata_root depotdata_root logdata_root
                sdp_p4superuser admin_pass_filename
                mailfrom maillist mailhost
                python
                keepckps keeplogs limit_one_daily_checkpoint""".split()
        instance_names = self.instances.keys()
        lines = []
        lines.append("# Global sdp_config.ini")
        lines.append("")
        for instance_name in instance_names:
            instance = self.instances[instance_name]
            lines.append("\n[%s:%s]" % (instance["sdp_instance"], instance["sdp_hostname"]))
            lines.append("%s=%s:%s" % ("p4port", instance["sdp_hostname"], instance["sdp_p4port_number"]))
            for setting in common_settings:
                lines.append("%s=%s" % (setting, instance[setting]))

        sdp_config_file = "sdp_config.ini"
        self.commands.append(sdp_config_file)
        self.logger.info("Config file written: %s" % sdp_config_file)
        with open(sdp_config_file, "w") as fh:
            for line in lines:
                fh.write("%s\n" % line)

    def get_configure_bat_contents(self):
        "Return the information to be written into configure bat files per instance"
        cmd_lines = {}   # indexed by instance name
        instance_names = self.instances.keys()
        master_name = self.get_master_instance_name()
        cmd_lines[master_name] = []
        master_instance = self.instances[master_name]

        p4cmd = "p4 -p %s:%s -u %s" % (master_instance["sdp_hostname"], master_instance["sdp_p4port_number"],
                                                      master_instance["sdp_p4superuser"])
        p4configurecmd = "%s configure set" % (p4cmd)
        path = joinpath(master_instance["depotdata_root"], "p4", master_instance["sdp_instance"],
                            "checkpoints", "p4_%s" % master_instance["sdp_instance"])
        cmd_lines[master_name].append("%s %s#journalPrefix=%s" % (p4configurecmd, master_instance["p4name"], path))
        cmd_lines[master_name].append("%s serverid %s" % (p4cmd, master_instance["p4name"]))
        log_files = {3: 'errors.csv', 7: 'events.csv', 8: 'integrity.csv'}
        for i in log_files.keys():
            cmd_lines[master_name].append("%s %s#serverlog.file.%d=%s" % (p4configurecmd, master_instance["p4name"], i,
                                            joinpath(master_instance["logs_dir"], log_files[i])))
            cmd_lines[master_name].append("%s %s#serverlog.retain.%d=7" % (p4configurecmd, master_instance["p4name"], i))

        for l in STANDARD_CONFIGURE_LINES:
            if l.strip():
                cmd_lines[master_name].append("%s %s" % (p4configurecmd, l.strip()))

        # Now set up all the config variables for replication
        for instance_name in [s for s in instance_names if s != master_name]:
            instance = self.instances[instance_name]
            if not instance["sdp_service_type"] in ["replica", "forwarding-replica", "build-server"]:
                continue
            path = joinpath(instance["depotdata_root"], "p4", instance["sdp_instance"], "checkpoints",
                                "p4_%s" % instance["sdp_instance"])
            cmd_lines[master_name].append("%s %s#journalPrefix=%s" % (p4configurecmd, instance["p4name"], path))
            path = joinpath(instance["depotdata_root"], "p4", instance["sdp_instance"], "p4tickets.txt")
            cmd_lines[master_name].append('%s %s#P4PORT=%s' % (p4configurecmd, instance["p4name"], instance["sdp_p4port_number"]))
            cmd_lines[master_name].append('%s %s#P4TARGET=%s:%s' % (p4configurecmd, instance["p4name"],
                                                   master_instance["sdp_hostname"],
                                                   master_instance["sdp_p4port_number"]))
            tickets_path = joinpath(instance["depotdata_dir"], "p4tickets.txt")
            cmd_lines[master_name].append('%s %s#P4TICKETS=%s' % (p4configurecmd, instance["p4name"], tickets_path))           
            log_path = joinpath(instance["logs_dir"], "%s.log" % instance["p4name"])
            cmd_lines[master_name].append('%s %s#P4LOG=%s' % (p4configurecmd, instance["p4name"], log_path))           
            cmd_lines[master_name].append('%s %s#server=3' % (p4configurecmd, instance["p4name"]))
            cmd_lines[master_name].append('%s "%s#startup.1=pull -i 1"' % (p4configurecmd, instance["p4name"]))
            for i in range(2, 6):
                cmd_lines[master_name].append('%s "%s#startup.%d=pull -u -i 1"' % (p4configurecmd, instance["p4name"], i))
            cmd_lines[master_name].append('%s "%s#lbr.replication=readonly"' % (p4configurecmd, instance["p4name"]))
            if instance["sdp_service_type"] in ["replica", "build-server", "forwarding-replica"]:
                cmd_lines[master_name].append('%s "%s#db.replication=readonly"' % (p4configurecmd, instance["p4name"]))
            if instance["sdp_service_type"] in ["forwarding-replica"]:
                cmd_lines[master_name].append('%s "%s#rpl.forward.all=1"' % (p4configurecmd, instance["p4name"]))
            cmd_lines[master_name].append('%s "%s#serviceUser=%s"' % (p4configurecmd, instance["p4name"], instance["p4name"]))

            if not instance.p4name in cmd_lines:
                cmd_lines[instance.p4name] = []
            line = "p4 -p %s:%s -u %s serverid %s" % (instance["sdp_hostname"], instance["sdp_p4port_number"],
                                                  instance["sdp_p4superuser"], instance["p4name"])
            cmd_lines[instance.p4name].append(line)
        return cmd_lines

    def write_configure_bat_contents(self, cmd_lines):
        "Write the appropriate configure bat files for respective instances"
        command_files = []
        for instance_name in cmd_lines.keys():
            command_file = "configure_%s.bat" % (instance_name)
            command_files.append(command_file)
            with open(command_file, "w") as fh:
                for line in cmd_lines[instance_name]:
                    fh.write("%s\n" % line)
        return command_files

    def copy_ignore_errors(self, sourcefile, dest):
        "Ignore errors when trying to copy file - target exists"
        try:
            shutil.copy(sourcefile, dest)
        except Exception as e:
            if not re.search("Permission denied:", str(e)):
                self.logger.info("Error copying '%s': %s" % (sourcefile, e))

    def get_instance_dirs(self):
        "Get a list of instance dirs valid on the current machine"
        dirlist = []
        instance_names = sorted(self.instances.keys())
        for instance_name in instance_names:
            instance = self.instances[instance_name]
            # Only create dirs when we are on the correct hostname
            if not instance.is_current_host():
                self.logger.info("Ignoring directories on '%s' for instance '%s'" % (instance["sdp_hostname"], instance["sdp_instance"]))
                continue
            dirlist.extend([
                instance.root_dir,
                instance.logdata_dir,
                instance.bin_dir,
                joinpath(instance.depotdata_dir, 'tmp'),
                joinpath(instance.depotdata_dir, 'depots'),
                joinpath(instance.depotdata_dir, 'checkpoints'),
                joinpath(instance.depotdata_dir, 'ssl'),
                instance.common_bin_dir,
                joinpath(instance.common_bin_dir, 'triggers'),
                joinpath(instance.metadata_dir, 'root', 'save'),
                joinpath(instance.metadata_dir, 'offline_db'),
                instance.logs_dir,
                instance.sdp_config_dir
                ])
        return dirlist

    def checkdirs(self, dirlist):
        "Check that the directories exist"
        pass

    def mkdirs(self, dirlist):
        "Make all appropriate directories on this machine"
        if not self.options.yes:
            self.logger.info("The following directories would be created with the -y/--yes flag")
            for mdir in dirlist:
                self.logger.info("  %s" % mdir)
            return
        for dir in dirlist:
            if not os.path.exists(dir):
                self.logger.info("Creating dir '%s'" % dir)
                os.makedirs(dir)
        instance_names = self.instances.keys()
        curr_scriptdir = os.path.dirname(os.path.realpath(__file__))
        for instance_name in instance_names:
            instance = self.instances[instance_name]
            # Only create dirs when we are on the correct hostname
            if not instance.is_current_host():
                self.logger.info("Ignoring directories on '%s' for instance '%s'" % (instance["sdp_hostname"], instance["sdp_instance"]))
                continue
            self.logger.info("Copying files on '%s' for instance '%s'" % (instance["sdp_hostname"], instance["sdp_instance"]))
            for filename in glob.glob(os.path.join(instance.sdp_common_bin_dir, '*.*')):
                self.logger.info("Copying '%s' to '%s'" % (filename, instance.common_bin_dir))
                self.copy_ignore_errors(filename, instance.common_bin_dir)
            for filename in glob.glob(os.path.join(instance.sdp_common_bin_dir, 'triggers', '*.*')):
                self.logger.info("Copying '%s' to '%s'" % (filename, os.path.join(instance.common_bin_dir, 'triggers')))
                self.copy_ignore_errors(filename, os.path.join(instance.common_bin_dir, 'triggers'))
            for filename in ['daily_backup.bat', 'p4verify.bat', 'replica_status.bat', 'sync_replica.bat',
                             'weekly_backup.bat', 'weekly_sync_replica.bat']:
                self.logger.info("Creating instance bat file '%s'" % (os.path.join(instance.bin_dir, filename)))
                self.create_instance_bat(instance.bin_dir, filename, instance['sdp_instance'], instance.common_bin_dir)
            self.copy_ignore_errors(os.path.join(curr_scriptdir, 'p4.exe'), instance.bin_dir)
            self.copy_ignore_errors(os.path.join(curr_scriptdir, 'p4d.exe'), instance.bin_dir)
            self.copy_ignore_errors(os.path.join(curr_scriptdir, 'p4d.exe'), os.path.join(instance.bin_dir, 'p4s.exe'))
            shutil.copy(os.path.join(curr_scriptdir, 'sdp_config.ini'), instance.sdp_config_dir)

    def bat_file_hostname_guard_lines(self, hostname):
        lines = ['@echo off',
            'FOR /F "usebackq" %%i IN (`hostname`) DO SET HOSTNAME=%%i',
            'if /i "%s" NEQ "%s" (' % ('%HOSTNAME%', hostname),
            '  echo ERROR: This command file should only be run on machine with hostname "%s"' % (hostname),
            '  exit /b 1',
            ')',
            '@echo on']
        return lines

    def get_service_install_cmds(self):
        "Configure any services on the current machine"
        cmds = {}
        instance_names = sorted(self.instances.keys())
        for instance_name in instance_names:
            instance = self.instances[instance_name]
            hostname = instance['sdp_hostname'].lower()
            self.logger.info("Creating service configure commands on '%s' for instance '%s' in install_services_%s.bat" % (
                        hostname, instance["sdp_instance"], hostname))

            # Install services
            if hostname not in cmds:
                cmds[hostname] = []

            instsrv = joinpath(instance['depotdata_root'], 'p4', 'common', 'bin', 'instsrv.exe')

            cmd = '%s p4_%s "%s"' % (instsrv, instance['sdp_instance'],
                                    os.path.join(instance.bin_dir, 'p4s.exe'))
            cmds[hostname].append(cmd)
            p4cmd = os.path.join(instance.bin_dir, 'p4.exe')
            cmds[hostname].append('%s set -S p4_%s P4ROOT=%s' % (p4cmd, instance['sdp_instance'], instance.root_dir))
            cmds[hostname].append('%s set -S p4_%s P4JOURNAL=%s' % (p4cmd, instance['sdp_instance'],
                                                       os.path.join(instance.logs_dir, 'journal')))
            cmds[hostname].append('%s set -S p4_%s P4NAME=%s' % (p4cmd, instance['sdp_instance'],
                                                       instance['p4name']))
            cmds[hostname].append('%s set -S p4_%s P4PORT=%s' % (p4cmd, instance['sdp_instance'],
                                                       instance['sdp_p4port_number']))
            log_path = joinpath(instance["logs_dir"], "%s.log" % instance["p4name"])
            cmds[hostname].append('%s set -S p4_%s P4LOG=%s' % (p4cmd, instance['sdp_instance'], log_path))
        return cmds

    def write_service_install_cmds(self, cmds):
        "Configure any services on the various machines"
        command_files = []
        if not cmds:
            return command_files
        for instance_name in self.instances:
            instance = self.instances[instance_name]
            hostname = instance["sdp_hostname"].lower()
            if hostname in cmds:
                command_file = "install_services_%s.bat" % hostname
                if not command_file in command_files:
                    command_files.append(command_file)
                with open(command_file, "w") as fh:
                    # Write a safeguarding header for specific hostname
                    lines = self.bat_file_hostname_guard_lines(hostname)
                    lines.extend(cmds[hostname])
                    for line in lines:
                        fh.write("%s\n" % line)
        return command_files

    def create_instance_bat(self, destdir, fname, instance, common_bin_dir):
        "Creates instance specific batch files which call common one"
        hdrlines = """::-----------------------------------------------------------------------------
            :: Copyright (c) 2012-2014 Perforce Software, Inc.  Provided for use as defined in
            :: the Perforce Consulting Services Agreement.
            ::-----------------------------------------------------------------------------

            set ORIG_DIR=%CD%
            """.split("\n")
        with open(os.path.join(destdir, fname), "w") as fh:
            for line in hdrlines:
                fh.write("%s\n" % line.strip())
            fh.write("\n")
            fh.write('cd /d "%s"\n' % common_bin_dir)
            fh.write('@call %s %s\n' % (fname, instance))
            fh.write('cd /d %ORIG_DIR%\n')

    # Validate:
    # - master server on this host first - then any replica servers
    # - server exists (so serverid)
    # - server is of correct type
    # - adminpass file exists
    # - adminpass file is correct
    # - any replica users exist as service users
    # - replica users have long password timeout
    # - configure variables are as expected (warn if different - not always an error)
    # - if on replica then P4TICKETS variables

    def report_config(self):
        "Reports on whether the current configuration is valid - talks to master server"
        if not self.options.report:
            return
        errors = []
        info = []
        p4 = P4.P4()
        m_name = self.get_master_instance_name()
        m_instance = self.instances[m_name]
        if m_instance.is_current_host():
            p4.port = m_instance.sdp_p4port_number
            p4.user = m_instance.sdp_p4superuser
            p4.connect()
            server_defined = False
            servers = p4.run_servers()
            m_svr = find_record(servers, 'ServerID', m_name)
            if not m_svr:
                errors.append("Error: no 'server' record defined for instance '%s' - use 'p4 server' to define" %
                            m_name)
            elif m_svr['Services'] != 'standard':
                errors.append("Error: 'server' record for instance '%s' must have 'Services: standard'" % m_name)
                info.append("Server record exists for master instance '%s'" % m_name)
        
            users = p4.run_users("-a")
            for i_name in [inst for inst in self.instances.keys() if inst != m_name]:
                instance = self.instances[i_name]
                svr = find_record(servers, 'ServerID', i_name)
                if not svr:
                    errors.append("Error: no 'server' record defined for instance '%s' - use 'p4 server' to define" %
                            i_name)
                else:
                    info.append("Server record exists for instance '%s'" % i_name)
                    if svr['Services'] != instance.sdp_service_type:
                        errors.append("Error: 'server' record for instance '%s' defines Services as '%s' - expected '%s'" %
                                (i_name, svr['Services'],instance.sdp_service_type))
                user = find_record(users, 'User', instance.p4name)
                if not user:
                    errors.append("Error: no 'user' record defined for instance '%s' - use 'p4 user' to define a service user" %
                            i_name)
                else:
                    info.append("User record exists for instance '%s'" % i_name)
                    if user['Type'] != 'service':
                        errors.append("Error: 'user' record for instance '%s' defines Type as '%s' - expected '%s'" %
                                (i_name, user['Type'], 'service'))
                    
            p4.disconnect()
                            
        print("\nThe following environment values were checked:")
        print("\n".join(info))
        if errors:
            print("\nThe following ERRORS/WARNINGS encountered:")
            print("\n".join(errors))
        else:
            print("\nNo ERRORS/WARNINGS found.")
            
    def process_config(self):
        "Process and produce the various files"
        self.isvalid_config()
        if self.options.yes and not running_as_administrator():
            raise SDPException("This action must be run with Administrator rights")
        if self.options.report:
            self.report_config()
            return
        self.write_master_config_ini()
        dirs = self.get_instance_dirs()
        if self.options.yes:
            self.mkdirs(dirs)
        else:
            self.checkdirs(dirs)
        cmds = self.get_service_install_cmds()
        command_files = self.write_service_install_cmds(cmds)
        cmd_lines = self.get_configure_bat_contents()
        command_files.extend(self.write_configure_bat_contents(cmd_lines))
        print("\n\n")
        if self.options.yes:
            print("Please run the following commands:")
        else:
            print("The following commands have been created - but you are in report mode so no directories have been created")
        for cmd in command_files:
            print("    %s" % cmd)
        print("You will also need to seed the replicas from a checkpoint and run the appropriate commands on those machines")

def main():
    "Initialization.  Process command line argument and initialize logging."
    sdpconfig = SDPConfig()
    sdpconfig.process_config()

if __name__ == '__main__':
    main()
