<commit_before>from mkconfig.core.transfig import ContextAwareTransfiguration, Jinja2FileTemplateTransfiguration, \
    YamlFileReaderToContextTransfiguration
from mkconfig.conf.context import *
from mkconfig.core.factory import TemplateEngineFactory
from mkconfig.core.jinja2 import Jinja2Engine
from mkconfig.env import Configurations
from mkconfig.core.chain import ChainOfTransfiguration
from mkconfig.conf.utils import Utils
from os import listdir
from os.path import isfile, join
import os.path
import yaml
import logging
import mkconfig.env


logger = logging.getLogger(__name__)


class PrepareAppConfTransfiguration(ContextAwareTransfiguration):
    """
    A context-enabled Transifguration that prepares the associated configuration file path for given a app name
    """

    def perform(self, context):
        appName = context[CTX_KEY_COLLECTD_JMX_APP_PREFIX]

        appConfYamlFile = context[CTX_KEY_COLLECTD_JMX_APP_CONF_DIR] + "collectd.jmx." + appName + '.conf.yaml'
        PrepareAppConfTransfiguration.validate_file_exist(appConfYamlFile)
        context[CTX_KEY_COLLECTD_JMX_CONF_YAML_FILE] = appConfYamlFile
        logger.info('Set the conf file for app [{0}] at [{1}]'.format(appName, appConfYamlFile))

        logger.debug("======================================================================")
        logger.debug('PrepareAppConf Transifiguration w/ appName [{}]'.format(appName))
        logger.debug("======================================================================")


    @staticmethod
    def validate_file_exist(file_path):
        if not os.path.isfile(file_path):
            raise IOError('File [{0}] not found !'.format(file_path))


class CollectdJmxConfToContextTransfiguration(YamlFileReaderToContextTransfiguration):
    """
    A YamlToContextTransfiguration that reads yaml file with respect to attr : _collectd_jmx_conf_yaml_file
    """

    def __init__(self, keyName = CTX_KEY_COLLECTD_JMX_CONF_YAML_FILE):
        super().__init__(keyName)

    def read_content(self, context):
        yaml_content = yaml.load(self._file)

        for key, value in yaml_content.items():
            context[key] = value

        for idx, mbean in enumerate(context['mbeans']):
            self.patch_mbean_table_value(mbean)
            #mbean is updated
            context['mbeans'][idx] = mbean


    def patch_mbean_table_value(self, mbean):
        for attribute in mbean['attributes']:
            if 'Table' in attribute:
                value = attribute['Table']
                attribute['Table'] = Utils.boolean_to_lowercase_literal(value)

        logger.debug(mbean)


class CollectdJmxTransTemplateToStubJinja2(Jinja2FileTemplateTransfiguration):
    """
    The first phase of  CollectdJmxTransfiguration to transform from input yaml files to stub configuration file (half-product)
    """

    def __init__(self):
        super().__init__()

    def perform(self, context):
        input = context[CTX_KEY_COLLECTD_JMX_TEMPLATE_FILE]
        intermediate_template = '_' + input + '.tmp'

        self._input = input
        self._output = Configurations.getTemplateFile(intermediate_template)
        super().perform(context)

        logger.debug("======================================================================")
        logger.debug('CollectdJmx Transifig Template->Stub @ [{}]'.format(self._output))
        logger.debug("======================================================================")




class CollectdJmxTransStubToConfiguration(Jinja2FileTemplateTransfiguration):
    """
    The second phase of CollectdJmxTransfiguration to transform from the stub file to a partial configuration for a specific appliation
    """

    def __init__(self):
        super().__init__()

    def perform(self, context):
        intermediate_template = '_' + context[CTX_KEY_COLLECTD_JMX_TEMPLATE_FILE] + '.tmp'

        output_filename = context[CTX_KEY_COLLECTD_JMX_APP_PREFIX] + '.output.partial'

        self._input = intermediate_template
        self._output = Configurations.getOutputFile(output_filename)
        super().perform(context)

        logger.debug("======================================================================")
        logger.debug('CollectdJmx Transifig Stub->Output @ [{}]'.format(self._output))
        logger.debug("======================================================================")


class CollectdJmxPartialTransifgurationChain(ChainOfTransfiguration):
    """
    An (inner) chain of transfiguration that transform input to a partial collectd jmx configuration
    """

    def __init__(self):
        super().__init__()
        TemplateEngineFactory.add_factory('Jinja2Engine', Jinja2Engine.Factory)

        self._step0 = PrepareAppConfTransfiguration()
        self._step1 = CollectdJmxConfToContextTransfiguration()
        self._step2 = CollectdJmxTransTemplateToStubJinja2()
        self._step3 = CollectdJmxTransStubToConfiguration()

        self.add(self._step0)
        self.add(self._step1)
        self.add(self._step2)
        self.add(self._step3)

        logger.info("PARTIAL CollectdJmx Transfiguration Chain COMPLETE")


class SplitAppConfTransfiguration(ContextAwareTransfiguration):
    """
    A outer chain that controls the whole process of performing config generation for each applicaiton and merge the partial results into a final output
    """
    def perform(self, context):
        listOfAppNames = context[CTX_KEY_COLLECTD_JMX_USER_SELECTED_APP_LIST].split()

        #FIXME: The distinguishment might not be necessary, as above always return a list of 0 or 1 element
        #distinguish between string object and list
        if isinstance(listOfAppNames, str):
            logger.info('Processing ONE app [%s]' %listOfAppNames)
            self.generateAppPartialConfiguration(context, listOfAppNames)
        else :
            logger.info('Processing list of apps [%s]' % listOfAppNames)
            for appName in listOfAppNames:
                self.generateAppPartialConfiguration(context, appName)

        logger.debug("======================================================================")
        logger.debug('Split app configuration w/ app list [{}]'.format(listOfAppNames))
        logger.debug("======================================================================")

    def generateAppPartialConfiguration(self, context, appName):
        logger.info('Spliting the partial configuraiton for [%s]' % appName)

        context[CTX_KEY_COLLECTD_JMX_APP_PREFIX] = appName
        inner_chain = CollectdJmxPartialTransifgurationChain()
        inner_chain.execute(context)


class CollectdJmxConsolidatePartialConfigurations(ContextAwareTransfiguration):
    """
    The final phase of CollectdJmxTransfiguration to consolidated a complete Collectd-Jmx configuration.
    """
    def __init__(self):
        super().__init__()

    def perform(self, context):

        header = """
LoadPlugin java

<Plugin "java">
    #JVMARG "-verbose:jni"
    JVMArg "-Djava.class.path=/usr/share/collectd/java/collectd-api.jar:/usr/share/collectd/java/generic-jmx.jar"

    LoadPlugin "org.collectd.java.GenericJMX"
        """

        footer = """


</Plugin><commit_msg>* Add optional jvm arguments to collectd-jmx (MKCONFIG-44)<commit_after>from mkconfig.core.transfig import ContextAwareTransfiguration, Jinja2FileTemplateTransfiguration, \
    YamlFileReaderToContextTransfiguration
from mkconfig.conf.context import *
from mkconfig.core.factory import TemplateEngineFactory
from mkconfig.core.jinja2 import Jinja2Engine
from mkconfig.env import Configurations
from mkconfig.core.chain import ChainOfTransfiguration
from mkconfig.conf.utils import Utils
from os import listdir
from os.path import isfile, join
import os.path
import yaml
import logging
import mkconfig.env


logger = logging.getLogger(__name__)


class PrepareAppConfTransfiguration(ContextAwareTransfiguration):
    """
    A context-enabled Transifguration that prepares the associated configuration file path for given a app name
    """

    def perform(self, context):
        appName = context[CTX_KEY_COLLECTD_JMX_APP_PREFIX]

        appConfYamlFile = context[CTX_KEY_COLLECTD_JMX_APP_CONF_DIR] + "collectd.jmx." + appName + '.conf.yaml'
        PrepareAppConfTransfiguration.validate_file_exist(appConfYamlFile)
        context[CTX_KEY_COLLECTD_JMX_CONF_YAML_FILE] = appConfYamlFile
        logger.info('Set the conf file for app [{0}] at [{1}]'.format(appName, appConfYamlFile))

        logger.debug("======================================================================")
        logger.debug('PrepareAppConf Transifiguration w/ appName [{}]'.format(appName))
        logger.debug("======================================================================")


    @staticmethod
    def validate_file_exist(file_path):
        if not os.path.isfile(file_path):
            raise IOError('File [{0}] not found !'.format(file_path))


class CollectdJmxConfToContextTransfiguration(YamlFileReaderToContextTransfiguration):
    """
    A YamlToContextTransfiguration that reads yaml file with respect to attr : _collectd_jmx_conf_yaml_file
    """

    def __init__(self, keyName = CTX_KEY_COLLECTD_JMX_CONF_YAML_FILE):
        super().__init__(keyName)

    def read_content(self, context):
        yaml_content = yaml.load(self._file)

        for key, value in yaml_content.items():
            context[key] = value

        for idx, mbean in enumerate(context['mbeans']):
            self.patch_mbean_table_value(mbean)
            #mbean is updated
            context['mbeans'][idx] = mbean


    def patch_mbean_table_value(self, mbean):
        for attribute in mbean['attributes']:
            if 'Table' in attribute:
                value = attribute['Table']
                attribute['Table'] = Utils.boolean_to_lowercase_literal(value)

        logger.debug(mbean)


class CollectdJmxTransTemplateToStubJinja2(Jinja2FileTemplateTransfiguration):
    """
    The first phase of  CollectdJmxTransfiguration to transform from input yaml files to stub configuration file (half-product)
    """

    def __init__(self):
        super().__init__()

    def perform(self, context):
        input = context[CTX_KEY_COLLECTD_JMX_TEMPLATE_FILE]
        intermediate_template = '_' + input + '.tmp'

        self._input = input
        self._output = Configurations.getTemplateFile(intermediate_template)
        super().perform(context)

        logger.debug("======================================================================")
        logger.debug('CollectdJmx Transifig Template->Stub @ [{}]'.format(self._output))
        logger.debug("======================================================================")




class CollectdJmxTransStubToConfiguration(Jinja2FileTemplateTransfiguration):
    """
    The second phase of CollectdJmxTransfiguration to transform from the stub file to a partial configuration for a specific appliation
    """

    def __init__(self):
        super().__init__()

    def perform(self, context):
        intermediate_template = '_' + context[CTX_KEY_COLLECTD_JMX_TEMPLATE_FILE] + '.tmp'

        output_filename = context[CTX_KEY_COLLECTD_JMX_APP_PREFIX] + '.output.partial'

        self._input = intermediate_template
        self._output = Configurations.getOutputFile(output_filename)
        super().perform(context)

        logger.debug("======================================================================")
        logger.debug('CollectdJmx Transifig Stub->Output @ [{}]'.format(self._output))
        logger.debug("======================================================================")


class CollectdJmxPartialTransifgurationChain(ChainOfTransfiguration):
    """
    An (inner) chain of transfiguration that transform input to a partial collectd jmx configuration
    """

    def __init__(self):
        super().__init__()
        TemplateEngineFactory.add_factory('Jinja2Engine', Jinja2Engine.Factory)

        self._step0 = PrepareAppConfTransfiguration()
        self._step1 = CollectdJmxConfToContextTransfiguration()
        self._step2 = CollectdJmxTransTemplateToStubJinja2()
        self._step3 = CollectdJmxTransStubToConfiguration()

        self.add(self._step0)
        self.add(self._step1)
        self.add(self._step2)
        self.add(self._step3)

        logger.info("PARTIAL CollectdJmx Transfiguration Chain COMPLETE")


class SplitAppConfTransfiguration(ContextAwareTransfiguration):
    """
    A outer chain that controls the whole process of performing config generation for each applicaiton and merge the partial results into a final output
    """
    def perform(self, context):
        listOfAppNames = context[CTX_KEY_COLLECTD_JMX_USER_SELECTED_APP_LIST].split()

        #FIXME: The distinguishment might not be necessary, as above always return a list of 0 or 1 element
        #distinguish between string object and list
        if isinstance(listOfAppNames, str):
            logger.info('Processing ONE app [%s]' %listOfAppNames)
            self.generateAppPartialConfiguration(context, listOfAppNames)
        else :
            logger.info('Processing list of apps [%s]' % listOfAppNames)
            for appName in listOfAppNames:
                self.generateAppPartialConfiguration(context, appName)

        logger.debug("======================================================================")
        logger.debug('Split app configuration w/ app list [{}]'.format(listOfAppNames))
        logger.debug("======================================================================")

    def generateAppPartialConfiguration(self, context, appName):
        logger.info('Spliting the partial configuraiton for [%s]' % appName)

        context[CTX_KEY_COLLECTD_JMX_APP_PREFIX] = appName
        inner_chain = CollectdJmxPartialTransifgurationChain()
        inner_chain.execute(context)


class CollectdJmxConsolidatePartialConfigurations(ContextAwareTransfiguration):
    """
    The final phase of CollectdJmxTransfiguration to consolidated a complete Collectd-Jmx configuration.
    """
    def __init__(self):
        super().__init__()

    def perform(self, context):

        header = """
LoadPlugin java

<Plugin "java">
    #JVMARG "-verbose:jni"
    #JVMARG "-Xmx128m"
    JVMArg "-Djava.class.path=/usr/share/collectd/java/collectd-api.jar:/usr/share/collectd/java/generic-jmx.jar"

    LoadPlugin "org.collectd.java.GenericJMX"
        """

        footer = """


</Plugin>
{'fraction_non_alphanumeric': 0.16002482073910645, 'fraction_numerical': 0.0033783783783783786, 'mean_word_length': 3.918616480162767, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2100823', 'n_tokens_mistral': 4047, 'n_tokens_neox': 3837, 'n_words': 850}