Example #1
0
def createEventFileSettings(tree, files):
    fileid = 0
    sysconfig = """
$ModLoad imfile

                """
    if not files:
        return
    for file in files:
        fileid = fileid + 1
        eventSourceElement = XmlUtil.createElement(eventSourceSchema)
        XmlUtil.setXmlValue(eventSourceElement, 'RouteEvent', 'eventName',
                            file["table"])
        eventSourceElement.set('source', 'ladfile' + str(fileid))
        XmlUtil.addElement(tree, 'Events/MdsdEvents', eventSourceElement)

        sourceElement = XmlUtil.createElement(sourceSchema)
        sourceElement.set('name', 'ladfile' + str(fileid))
        XmlUtil.addElement(tree, 'Sources', sourceElement)

        syslog_config = syslogEventSourceConfig.replace('#FILE#', file['file'])
        syslog_config = syslog_config.replace('#STATFILE#',
                                              file['file'].replace("/", "-"))
        syslog_config = syslog_config.replace('#FILETAG#',
                                              'ladfile' + str(fileid))
        sysconfig += syslog_config
    return sysconfig
    def _update_account_settings(self, account, token, endpoints):
        """
        Update the MDSD configuration Account element with Azure table storage properties.
        Exactly one of (key, token) must be provided.
        :param account: Storage account to which LAD should write data
        :param token: SAS token to access the storage account
        :param endpoints: Identifies the Azure storage endpoints (public or specific sovereign cloud) where the storage account is
        """
        assert token, "Token must be given."
        assert self._mdsd_config_xml_tree is not None

        token = self._encrypt_secret_with_cert(token)
        assert token, "Could not encrypt token"
        XmlUtil.setXmlValue(self._mdsd_config_xml_tree,
                            'Accounts/SharedAccessSignature', "account",
                            account, ['isDefault', 'true'])
        XmlUtil.setXmlValue(self._mdsd_config_xml_tree,
                            'Accounts/SharedAccessSignature', "key", token,
                            ['isDefault', 'true'])
        XmlUtil.setXmlValue(self._mdsd_config_xml_tree,
                            'Accounts/SharedAccessSignature', "decryptKeyPath",
                            self._pkey_path, ['isDefault', 'true'])
        XmlUtil.setXmlValue(self._mdsd_config_xml_tree,
                            'Accounts/SharedAccessSignature', "tableEndpoint",
                            endpoints[0], ['isDefault', 'true'])
        XmlUtil.setXmlValue(self._mdsd_config_xml_tree,
                            'Accounts/SharedAccessSignature', "blobEndpoint",
                            endpoints[1], ['isDefault', 'true'])
        XmlUtil.removeElement(self._mdsd_config_xml_tree, 'Accounts',
                              'Account')
    def _set_event_volume(self, lad_cfg):
        """
        Set event volume in mdsd config. Check if desired event volume is specified,
        first in ladCfg then in public config. If in neither then default to Medium.
        :param lad_cfg: 'ladCfg' Json object to look up for the event volume setting.
        :return: None. The mdsd config XML tree's eventVolume attribute is directly updated.
        :rtype: str
        """
        assert self._mdsd_config_xml_tree is not None

        event_volume = LadUtil.getEventVolumeFromLadCfg(lad_cfg)
        if event_volume:
            self._logger_log("Event volume found in ladCfg: " + event_volume)
        else:
            event_volume = self._ext_settings.read_public_config("eventVolume")
            if event_volume:
                self._logger_log("Event volume found in public config: " +
                                 event_volume)
            else:
                event_volume = "Medium"
                self._logger_log(
                    "Event volume not found in config. Using default value: " +
                    event_volume)
        XmlUtil.setXmlValue(self._mdsd_config_xml_tree, "Management",
                            "eventVolume", event_volume)
Example #4
0
def createPortalSettings(tree, resourceId):
    portal_config = ET.ElementTree()
    portal_config.parse(os.path.join(WorkDir, "portal.xml.template"))
    XmlUtil.setXmlValue(portal_config, "./DerivedEvents/DerivedEvent/LADQuery",
                        "partitionKey", resourceId)
    XmlUtil.addElement(tree, 'Events', portal_config._root.getchildren()[0])
    XmlUtil.addElement(tree, 'Events', portal_config._root.getchildren()[1])
def configSettings():
    mdsdCfgstr = readPublicConfig('mdsdCfg')
    if not mdsdCfgstr :
        with open (os.path.join(WorkDir, './mdsdConfig.xml.template'),"r") as defaulCfg:
            mdsdCfgstr = defaulCfg.read()
    else:
        mdsdCfgstr = base64.b64decode(mdsdCfgstr)
    mdsdCfg = ET.ElementTree()
    mdsdCfg._setroot(XmlUtil.createElement(mdsdCfgstr))

    # update deployment id
    deployment_id = get_deployment_id()
    XmlUtil.setXmlValue(mdsdCfg, "Management/Identity/IdentityComponent", "", deployment_id, ["name", "DeploymentId"])

    try:
        resourceId = getResourceId()
        if resourceId:
            createPortalSettings(mdsdCfg,escape(resourceId))
            instanceID=""
            if resourceId.find("providers/Microsoft.Compute/virtualMachineScaleSets") >=0:
                instanceID = readUUID();
            config(mdsdCfg,"instanceID",instanceID,"Events/DerivedEvents/DerivedEvent/LADQuery")

    except Exception, e:
        hutil.error("Failed to create portal config  error:{0} {1}".format(e,traceback.format_exc()))
    def _update_and_get_file_monitoring_settings(self, files):
        """
        Update mdsd config's file monitoring config. Also creates/returns rsyslog imfile config.
        All the operations are based on the input param files, which is a Json-deserialized dictionary
        corresponding the following Json array example:
        [
            {"file":"/var/log/a.log", "table":"aLog"},
            {"file":"/var/log/b.log", "table":"bLog"}
        ]
        :param files: Array of dictionaries deserialized from the 'fileCfg' Json config (example as above)
        :return: rsyslog omfile module config file content
        """
        assert self._mdsd_config_xml_tree is not None

        if not files:
            return ''

        file_id = 0
        imfile_config = """
$ModLoad imfile

"""

        mdsd_event_source_schema = """
<MdsdEventSource source="ladfile">
    <RouteEvent dontUsePerNDayTable="true" eventName="" priority="High"/>
</MdsdEventSource>
"""

        mdsd_source_schema = """
<Source name="ladfile1" schema="ladfile" />
"""
        imfile_per_file_config_template = """
$InputFileName #FILE#
$InputFileTag #FILETAG#
$InputFileFacility local6
$InputFileStateFile syslog-stat#STATFILE#
$InputFileSeverity debug
$InputRunFileMonitor
"""

        for item in files:
            file_id += 1
            mdsd_event_source_element = XmlUtil.createElement(mdsd_event_source_schema)
            XmlUtil.setXmlValue(mdsd_event_source_element, 'RouteEvent', 'eventName', item["table"])
            mdsd_event_source_element.set('source', 'ladfile'+str(file_id))
            XmlUtil.addElement(self._mdsd_config_xml_tree, 'Events/MdsdEvents', mdsd_event_source_element)

            mdsd_source_element = XmlUtil.createElement(mdsd_source_schema)
            mdsd_source_element.set('name', 'ladfile'+str(file_id))
            XmlUtil.addElement(self._mdsd_config_xml_tree, 'Sources', mdsd_source_element)

            imfile_per_file_config = imfile_per_file_config_template.replace('#FILE#', item['file'])
            imfile_per_file_config = imfile_per_file_config.replace('#STATFILE#', item['file'].replace("/","-"))
            imfile_per_file_config = imfile_per_file_config.replace('#FILETAG#', 'ladfile'+str(file_id))
            imfile_config += imfile_per_file_config
        return imfile_config
def setEventVolume(mdsdCfg,ladCfg):
    eventVolume = LadUtil.getEventVolumeFromLadCfg(ladCfg)
    if eventVolume:
        hutil.log("Event volume found in ladCfg: " + eventVolume)
    else:
        eventVolume = readPublicConfig("eventVolume")
        if eventVolume:
            hutil.log("Event volume found in public config: " + eventVolume)
        else:
            eventVolume = "Medium"
            hutil.log("Event volume not found in config. Using default value: " + eventVolume)
            
    XmlUtil.setXmlValue(mdsdCfg,"Management","eventVolume",eventVolume)
    def _add_portal_settings(self, resource_id):
        """
        Update mdsd_config_xml_tree for Azure Portal metric collection setting.
        It's basically applying the resource_id as the partitionKey attribute of LADQuery elements.

        :param resource_id: ARM rerousce ID to provide as partitionKey in LADQuery elements
        :return: None
        """
        assert self._mdsd_config_xml_tree is not None

        portal_config = ET.ElementTree()
        portal_config.parse(os.path.join(self._ext_dir, 'portal.xml.template'))
        XmlUtil.setXmlValue(portal_config, './DerivedEvents/DerivedEvent/LADQuery', 'partitionKey', resource_id)
        XmlUtil.addElement(self._mdsd_config_xml_tree, 'Events', portal_config._root.getchildren()[0])
        XmlUtil.addElement(self._mdsd_config_xml_tree, 'Events', portal_config._root.getchildren()[1])
    def _set_xml_attr(self, key, value, xml_path, selector=[]):
        """
        Set XML attribute on the element specified with xml_path.
        :param key: The attribute name to set on the XML element.
        :param value: The default value to be set, if there's no public config for that attribute.
        :param xml_path: The path of the XML element(s) to which the attribute is applied.
        :param selector: Selector for finding the actual XML element (see XmlUtil.setXmlValue)
        :return: None. Change is directly applied to mdsd_config_xml_tree XML member object.
        """
        assert self._mdsd_config_xml_tree is not None

        v = self._ext_settings.read_public_config(key)
        if not v:
            v = value
        XmlUtil.setXmlValue(self._mdsd_config_xml_tree, xml_path, key, v, selector)
    def _set_xml_attr(self, key, value, xml_path, selector=[]):
        """
        Set XML attribute on the element specified with xml_path.
        :param key: The attribute name to set on the XML element.
        :param value: The default value to be set, if there's no public config for that attribute.
        :param xml_path: The path of the XML element(s) to which the attribute is applied.
        :param selector: Selector for finding the actual XML element (see XmlUtil.setXmlValue)
        :return: None. Change is directly applied to mdsd_config_xml_tree XML member object.
        """
        assert self._mdsd_config_xml_tree is not None

        v = self._ext_settings.read_public_config(key)
        if not v:
            v = value
        XmlUtil.setXmlValue(self._mdsd_config_xml_tree, xml_path, key, v, selector)
def setEventVolume(mdsdCfg, ladCfg):
    """
    Set event volume in mdsd config. Check if desired event volume is specified, first in ladCfg then in public config.
    :param mdsdCfg: The XML document holding the mdsd config
    :param ladCfg: The ladCfg part of the public config
    :return: The event volume. If not set anywhere in public config, return "Medium"
    :rtype: str
    """
    eventVolume = LadUtil.getEventVolumeFromLadCfg(ladCfg)
    if eventVolume:
        hutil.log("Event volume found in ladCfg: " + eventVolume)
    else:
        eventVolume = readPublicConfig("eventVolume")
        if eventVolume:
            hutil.log("Event volume found in public config: " + eventVolume)
        else:
            eventVolume = "Medium"
            hutil.log("Event volume not found in config. Using default value: " + eventVolume)

    XmlUtil.setXmlValue(mdsdCfg, "Management", "eventVolume", eventVolume)
    def _set_event_volume(self, lad_cfg):
        """
        Set event volume in mdsd config. Check if desired event volume is specified,
        first in ladCfg then in public config. If in neither then default to Medium.
        :param lad_cfg: 'ladCfg' Json object to look up for the event volume setting.
        :return: None. The mdsd config XML tree's eventVolume attribute is directly updated.
        :rtype: str
        """
        assert self._mdsd_config_xml_tree is not None

        event_volume = LadUtil.getEventVolumeFromLadCfg(lad_cfg)
        if event_volume:
            self._logger_log("Event volume found in ladCfg: " + event_volume)
        else:
            event_volume = self._ext_settings.read_public_config("eventVolume")
            if event_volume:
                self._logger_log("Event volume found in public config: " + event_volume)
            else:
                event_volume = "Medium"
                self._logger_log("Event volume not found in config. Using default value: " + event_volume)
        XmlUtil.setXmlValue(self._mdsd_config_xml_tree, "Management", "eventVolume", event_volume)
def createAccountSettings(tree,account,key,endpoint,aikey=None):
    XmlUtil.setXmlValue(tree,'Accounts/Account',"account",account,['isDefault','true'])
    XmlUtil.setXmlValue(tree,'Accounts/Account',"key",key,['isDefault','true'])
    XmlUtil.setXmlValue(tree,'Accounts/Account',"tableEndpoint",endpoint,['isDefault','true'])
    
    if aikey:
        AIUtil.createAccountElement(tree,aikey)
def createEventFileSettings(tree,files):
    fileid = 0;
    sysconfig = """
$ModLoad imfile

                """
    if not files:
        return
    for file in files:
        fileid=fileid+1
        eventSourceElement = XmlUtil.createElement(eventSourceSchema)
        XmlUtil.setXmlValue(eventSourceElement,'RouteEvent','eventName',file["table"])
        eventSourceElement.set('source','ladfile'+str(fileid))
        XmlUtil.addElement(tree,'Events/MdsdEvents',eventSourceElement)

        sourceElement = XmlUtil.createElement(sourceSchema)
        sourceElement.set('name','ladfile'+str(fileid))
        XmlUtil.addElement(tree,'Sources',sourceElement)

        syslog_config = syslogEventSourceConfig.replace('#FILE#',file['file'])
        syslog_config = syslog_config.replace('#STATFILE#',file['file'].replace("/","-"))
        syslog_config = syslog_config.replace('#FILETAG#','ladfile'+str(fileid))
        sysconfig+=syslog_config
    return sysconfig
    def _update_account_settings(self, account, token, endpoints):
        """
        Update the MDSD configuration Account element with Azure table storage properties.
        Exactly one of (key, token) must be provided.
        :param account: Storage account to which LAD should write data
        :param token: SAS token to access the storage account
        :param endpoints: Identifies the Azure storage endpoints (public or specific sovereign cloud) where the storage account is
        """
        assert token, "Token must be given."
        assert self._mdsd_config_xml_tree is not None

        token = self._encrypt_secret_with_cert(token)
        assert token, "Could not encrypt token"
        XmlUtil.setXmlValue(self._mdsd_config_xml_tree, 'Accounts/SharedAccessSignature',
                            "account", account, ['isDefault', 'true'])
        XmlUtil.setXmlValue(self._mdsd_config_xml_tree, 'Accounts/SharedAccessSignature',
                            "key", token, ['isDefault', 'true'])
        XmlUtil.setXmlValue(self._mdsd_config_xml_tree, 'Accounts/SharedAccessSignature',
                            "decryptKeyPath", self._pkey_path, ['isDefault', 'true'])
        XmlUtil.setXmlValue(self._mdsd_config_xml_tree, 'Accounts/SharedAccessSignature',
                            "tableEndpoint", endpoints[0], ['isDefault', 'true'])
        XmlUtil.setXmlValue(self._mdsd_config_xml_tree, 'Accounts/SharedAccessSignature',
                            "blobEndpoint", endpoints[1], ['isDefault', 'true'])
        XmlUtil.removeElement(self._mdsd_config_xml_tree, 'Accounts', 'Account')
def createEventFileSettings(tree, files):
    fileid = 0
    sysconfig = """
$ModLoad imfile

                """
    if not files:
        return
    for file in files:
        fileid = fileid + 1
        eventSourceElement = XmlUtil.createElement(eventSourceSchema)
        XmlUtil.setXmlValue(eventSourceElement, "RouteEvent", "eventName", file["table"])
        eventSourceElement.set("source", "ladfile" + str(fileid))
        XmlUtil.addElement(tree, "Events/MdsdEvents", eventSourceElement)

        sourceElement = XmlUtil.createElement(sourceSchema)
        sourceElement.set("name", "ladfile" + str(fileid))
        XmlUtil.addElement(tree, "Sources", sourceElement)

        syslog_config = syslogEventSourceConfig.replace("#FILE#", file["file"])
        syslog_config = syslog_config.replace("#STATFILE#", file["file"].replace("/", "-"))
        syslog_config = syslog_config.replace("#FILETAG#", "ladfile" + str(fileid))
        sysconfig += syslog_config
    return sysconfig
def config(xmltree,key,value,xmlpath,selector=[]):
    v = readPublicConfig(key)
    if not v:
        v = value
    XmlUtil.setXmlValue(xmltree,xmlpath,key,v,selector)
def createPortalSettings(tree,resourceId):
    portal_config = ET.ElementTree()
    portal_config.parse(os.path.join(WorkDir, "portal.xml.template"))
    XmlUtil.setXmlValue(portal_config,"./DerivedEvents/DerivedEvent/LADQuery","partitionKey",resourceId)
    XmlUtil.addElement(tree,'Events',portal_config._root.getchildren()[0])
    XmlUtil.addElement(tree,'Events',portal_config._root.getchildren()[1])
def configSettings():
    '''
    Generates XML cfg file for mdsd, from JSON config settings (public & private).
    Returns (True, '') if config was valid and proper xmlCfg.xml was generated.
    Returns (False, '...') if config was invalid and the error message.
    '''
    mdsdCfgstr = readPublicConfig('mdsdCfg')
    if not mdsdCfgstr:
        with open(os.path.join(WorkDir, './mdsdConfig.xml.template'),"r") as defaulCfg:
            mdsdCfgstr = defaulCfg.read()
    else:
        mdsdCfgstr = base64.b64decode(mdsdCfgstr)
    mdsdCfg = ET.ElementTree()
    mdsdCfg._setroot(XmlUtil.createElement(mdsdCfgstr))

    # update deployment id
    deployment_id = get_deployment_id()
    XmlUtil.setXmlValue(mdsdCfg, "Management/Identity/IdentityComponent", "", deployment_id, ["name", "DeploymentId"])

    try:
        resourceId = getResourceId()
        if resourceId:
            createPortalSettings(mdsdCfg,escape(resourceId))
            instanceID=""
            if resourceId.find("providers/Microsoft.Compute/virtualMachineScaleSets") >=0:
                instanceID = readUUID()
            config(mdsdCfg,"instanceID",instanceID,"Events/DerivedEvents/DerivedEvent/LADQuery")

    except Exception as e:
        hutil.error("Failed to create portal config  error:{0} {1}".format(e,traceback.format_exc()))
    
    # Check if Application Insights key is present in ladCfg
    ladCfg = readPublicConfig('ladCfg')
    try:
        aikey = AIUtil.tryGetAiKey(ladCfg)
        if aikey:
            hutil.log("Application Insights key found.")
        else:
            hutil.log("Application Insights key not found.")
    except Exception as e:
        hutil.error("Failed check for Application Insights key in LAD configuration with exception:{0} {1}".format(e,traceback.format_exc()))

    generatePerformanceCounterConfiguration(mdsdCfg,aikey != None)

    syslogCfg = getSyslogCfg()
    fileCfg = getFileCfg() 
    #fileCfg = [{"file":"/var/log/waagent.log","table":"waagent"},{"file":"/var/log/waagent2.log","table":"waagent3"}]
    try:
        if fileCfg:
           syslogCfg = createEventFileSettings(mdsdCfg,fileCfg)+syslogCfg

        with open(omfileconfig,'w') as hfile:
                hfile.write(syslogCfg)
    except Exception as e:
        hutil.error("Failed to create syslog_file config  error:{0} {1}".format(e,traceback.format_exc()))

    account = readPrivateConfig('storageAccountName')
    if not account:
        return False, "Empty storageAccountName"
    key = readPrivateConfig('storageAccountKey')
    if not key:
        return False, "Empty storageAccountKey"
    endpoint = readPrivateConfig('endpoint')
    if not endpoint:
        endpoint = 'table.core.windows.net'
    endpoint = 'https://'+account+"."+endpoint

    createAccountSettings(mdsdCfg,account,key,endpoint,aikey)

    # Check and add new syslog RouteEvent for Application Insights.
    if aikey:
        AIUtil.createSyslogRouteEventElement(mdsdCfg)

    setEventVolume(mdsdCfg,ladCfg)

    config(mdsdCfg,"sampleRateInSeconds","60","Events/OMI/OMIQuery")

    mdsdCfg.write(os.path.join(WorkDir, './xmlCfg.xml'))

    return True, ""
    def generate_all_configs(self):
        """
        Generates configs for all components required by LAD.
        Generates XML cfg file for mdsd, from JSON config settings (public & private).
        Also generates rsyslog/syslog-ng configs corresponding to 'syslogEvents' or 'syslogCfg' setting.
        Also generates fluentd's syslog/tail src configs and out_mdsd configs.
        The rsyslog/syslog-ng and fluentd configs are not yet saved to files. They are available through
        the corresponding getter methods of this class (get_fluentd_*_config(), get_*syslog*_config()).

        Returns (True, '') if config was valid and proper xmlCfg.xml was generated.
        Returns (False, '...') if config was invalid and the error message.
        """

        # 1. Add DeploymentId (if available) to identity columns
        if self._deployment_id:
            XmlUtil.setXmlValue(self._mdsd_config_xml_tree,
                                "Management/Identity/IdentityComponent", "",
                                self._deployment_id, ["name", "DeploymentId"])

        # 2. Generate telegraf, MetricsExtension, omsagent (fluentd) configs, rsyslog/syslog-ng config, and update corresponding mdsd config XML
        try:
            lad_cfg = self._ladCfg()
            if not lad_cfg:
                return False, 'Unable to find Ladcfg element. Failed to generate configs for fluentd, syslog, and mdsd ' \
                          '(see extension error logs for more details)'

            syslogEvents_setting = self._ext_settings.get_syslogEvents_setting(
            )
            fileLogs_setting = self._ext_settings.get_fileLogs_setting()
            lad_logging_config_helper = LadLoggingConfig(
                syslogEvents_setting, fileLogs_setting, self._sink_configs,
                self._pkey_path, self._cert_path, self._encrypt_secret)
            mdsd_syslog_config = lad_logging_config_helper.get_mdsd_syslog_config(
                self._ext_settings.read_protected_config(
                    'disableStorageAccount') == True)
            mdsd_filelog_config = lad_logging_config_helper.get_mdsd_filelog_config(
            )
            copy_source_mdsdevent_eh_url_elems(self._mdsd_config_xml_tree,
                                               mdsd_syslog_config)
            copy_source_mdsdevent_eh_url_elems(self._mdsd_config_xml_tree,
                                               mdsd_filelog_config)
            self._fluentd_syslog_src_config = lad_logging_config_helper.get_fluentd_syslog_src_config(
            )
            self._fluentd_tail_src_config = lad_logging_config_helper.get_fluentd_filelog_src_config(
            )
            self._fluentd_out_mdsd_config = lad_logging_config_helper.get_fluentd_out_mdsd_config(
            )
            self._rsyslog_config = lad_logging_config_helper.get_rsyslog_config(
            )
            self._syslog_ng_config = lad_logging_config_helper.get_syslog_ng_config(
            )
            parsed_perf_settings = lad_logging_config_helper.parse_lad_perf_settings(
                lad_cfg)
            self._telegraf_config, self._telegraf_namespaces = telhandler.handle_config(
                parsed_perf_settings, self._telegraf_me_url,
                self._telegraf_mdsd_url, True)

            #Handle the EH, JsonBlob and AzMonSink logic
            self._update_metric_collection_settings(lad_cfg,
                                                    self._telegraf_namespaces)
            mdsd_telegraf_config = lad_logging_config_helper.get_mdsd_telegraf_config(
                self._telegraf_namespaces)
            copy_source_mdsdevent_eh_url_elems(self._mdsd_config_xml_tree,
                                               mdsd_telegraf_config)

            resource_id = self._ext_settings.get_resource_id()
            if resource_id:
                # Set JsonBlob sink-related elements
                uuid_for_instance_id = self._fetch_uuid()
                self._add_obo_field(name='resourceId', value=resource_id)
                self._add_obo_field(name='agentIdentityHash',
                                    value=uuid_for_instance_id)

                XmlUtil.setXmlValue(
                    self._mdsd_config_xml_tree,
                    'Events/DerivedEvents/DerivedEvent/LADQuery',
                    'partitionKey', escape_nonalphanumerics(resource_id))
                lad_query_instance_id = ""
                if resource_id.find(
                        "providers/Microsoft.Compute/virtualMachineScaleSets"
                ) >= 0:
                    lad_query_instance_id = uuid_for_instance_id
                self._set_xml_attr(
                    "instanceID", lad_query_instance_id,
                    "Events/DerivedEvents/DerivedEvent/LADQuery")
            else:
                return False, 'Unable to find resource id in the config. Failed to generate configs for Metrics in mdsd ' \
                        '(see extension error logs for more details)'

            #Only enable Metrics if AzMonSink is in the config
            if self._enable_metrics_extension:
                me_handler.setup_me(True)

        except Exception as e:
            self._logger_error(
                "Failed to create omsagent (fluentd), rsyslog/syslog-ng configs, telegraf config or to update "
                "corresponding mdsd config XML. Error: {0}\nStacktrace: {1}".
                format(e, traceback.format_exc()))
            return False, 'Failed to generate configs for fluentd, syslog, and mdsd; see extension.log for more details.'

        # 3. Before starting to update the storage account settings, log extension's entire settings
        #    with secrets redacted, for diagnostic purpose.
        self._ext_settings.log_ext_settings_with_secrets_redacted(
            self._logger_log, self._logger_error)

        # 4. Actually update the storage account settings on mdsd config XML tree (based on extension's
        #    protectedSettings).
        account = self._ext_settings.read_protected_config(
            'storageAccountName').strip()
        if not account:
            return False, "Configuration Error: Must specify storageAccountName in protected settings. For information on protected settings, " \
                          "visit https://docs.microsoft.com/en-us/azure/virtual-machines/extensions/diagnostics-linux#protected-settings."
        if self._ext_settings.read_protected_config('storageAccountKey'):
            return False, "Configuration Error: The storageAccountKey protected setting is deprecated in LAD 3.0 and cannot be used. " \
                          "Instead, use the storageAccountSasToken setting. For documentation of this setting and instructions for generating " \
                          "a SAS token, visit https://docs.microsoft.com/en-us/azure/virtual-machines/extensions/diagnostics-linux#protected-settings."
        token = self._ext_settings.read_protected_config(
            'storageAccountSasToken').strip()
        if not token or token == '?':
            return False, "Configuration Error: Must specify storageAccountSasToken in the protected settings. For documentation of this setting and instructions " \
                          "for generating a SAS token, visit https://docs.microsoft.com/en-us/azure/virtual-machines/extensions/diagnostics-linux#protected-settings."
        if '?' == token[0]:
            token = token[1:]
        endpoints = get_storage_endpoints_with_account(
            account,
            self._ext_settings.read_protected_config('storageAccountEndPoint'))
        self._update_account_settings(account, token, endpoints)

        # 5. Update mdsd config XML's eventVolume attribute based on the logic specified in the helper.
        self._set_event_volume(lad_cfg)

        # 6. Finally generate mdsd config XML file out of the constructed XML tree object.
        self._mdsd_config_xml_tree.write(
            os.path.join(self._ext_dir, 'xmlCfg.xml'))

        return True, ""
    def _update_account_settings(self, account, key, token, endpoint, aikey=None):
        """
        Update the MDSD configuration Account element with Azure table storage properties.
        Exactly one of (key, token) must be provided. If an aikey is passed, then add a new Account element for Application
        Insights with the application insights key.
        :param account: Storage account to which LAD should write data
        :param key: Shared key secret for the storage account, if present
        :param token: SAS token to access the storage account, if present
        :param endpoint: Identifies the Azure instance (public or specific sovereign cloud) where the storage account is
        :param aikey: Key for accessing AI, if present
        """
        assert key or token, "Either key or token must be given."
        assert self._mdsd_config_xml_tree is not None

        handler_cert_path, handler_pkey_path = self._get_handler_cert_pkey_paths(self._ext_settings.get_handler_settings())
        if key:
            key = self._encrypt_secret_with_cert(handler_cert_path, key)
            XmlUtil.setXmlValue(self._mdsd_config_xml_tree, 'Accounts/Account',
                                "account", account, ['isDefault', 'true'])
            XmlUtil.setXmlValue(self._mdsd_config_xml_tree, 'Accounts/Account',
                                "key", key, ['isDefault', 'true'])
            XmlUtil.setXmlValue(self._mdsd_config_xml_tree, 'Accounts/Account',
                                "decryptKeyPath", handler_pkey_path, ['isDefault', 'true'])
            XmlUtil.setXmlValue(self._mdsd_config_xml_tree, 'Accounts/Account',
                                "tableEndpoint", endpoint, ['isDefault', 'true'])
            XmlUtil.removeElement(self._mdsd_config_xml_tree, 'Accounts', 'SharedAccessSignature')
        else:  # token
            token = self._encrypt_secret_with_cert(handler_cert_path, token)
            XmlUtil.setXmlValue(self._mdsd_config_xml_tree, 'Accounts/SharedAccessSignature',
                                "account", account, ['isDefault', 'true'])
            XmlUtil.setXmlValue(self._mdsd_config_xml_tree, 'Accounts/SharedAccessSignature',
                                "key", token, ['isDefault', 'true'])
            XmlUtil.setXmlValue(self._mdsd_config_xml_tree, 'Accounts/SharedAccessSignature',
                                "decryptKeyPath", handler_pkey_path, ['isDefault', 'true'])
            XmlUtil.setXmlValue(self._mdsd_config_xml_tree, 'Accounts/SharedAccessSignature',
                                "tableEndpoint", endpoint, ['isDefault', 'true'])
            XmlUtil.removeElement(self._mdsd_config_xml_tree, 'Accounts', 'Account')

        if aikey:
            AIUtil.createAccountElement(self._mdsd_config_xml_tree, aikey)
    def generate_all_configs(self):
        """
        Generates configs for all components required by LAD.
        Generates XML cfg file for mdsd, from JSON config settings (public & private).
        Also generates rsyslog/syslog-ng configs corresponding to 'syslogEvents' or 'syslogCfg' setting.
        Also generates fluentd's syslog/tail src configs and out_mdsd configs.
        The rsyslog/syslog-ng and fluentd configs are not yet saved to files. They are available through
        the corresponding getter methods of this class (get_fluentd_*_config(), get_*syslog*_config()).

        Returns (True, '') if config was valid and proper xmlCfg.xml was generated.
        Returns (False, '...') if config was invalid and the error message.
        """

        # 1. Add DeploymentId (if available) to identity columns
        if self._deployment_id:
            XmlUtil.setXmlValue(self._mdsd_config_xml_tree, "Management/Identity/IdentityComponent", "",
                                self._deployment_id, ["name", "DeploymentId"])
        # 2. Use ladCfg to generate OMIQuery and LADQuery elements
        lad_cfg = self._ladCfg()
        if lad_cfg:
            try:
                self._update_metric_collection_settings(lad_cfg)
                resource_id = self._ext_settings.get_resource_id()
                if resource_id:
                    XmlUtil.setXmlValue(self._mdsd_config_xml_tree, 'Events/DerivedEvents/DerivedEvent/LADQuery',
                                        'partitionKey', escape_nonalphanumerics(resource_id))
                    lad_query_instance_id = ""
                    uuid_for_instance_id = self._fetch_uuid()
                    if resource_id.find("providers/Microsoft.Compute/virtualMachineScaleSets") >= 0:
                        lad_query_instance_id = uuid_for_instance_id
                    self._set_xml_attr("instanceID", lad_query_instance_id, "Events/DerivedEvents/DerivedEvent/LADQuery")
                    # Set JsonBlob sink-related elements
                    self._add_obo_field(name='resourceId', value=resource_id)
                    self._add_obo_field(name='agentIdentityHash', value=uuid_for_instance_id)

            except Exception as e:
                self._logger_error("Failed to create portal config  error:{0} {1}".format(e, traceback.format_exc()))
                return False, 'Failed to create portal config from ladCfg (see extension error logs for more details)'

        # 3. Generate config for perfCfg. Need to distinguish between non-AppInsights scenario and AppInsights scenario,
        #    so check if Application Insights key is present and pass it to the actual helper
        #    function (self._apply_perf_cfg()).
        try:
            self._apply_perf_cfg()
        except Exception as e:
            self._logger_error("Failed check for Application Insights key in LAD configuration with exception:{0}\n"
                               "Stacktrace: {1}".format(e, traceback.format_exc()))
            return False, 'Failed to update perf counter config (see extension error logs for more details)'

        # 4. Generate omsagent (fluentd) configs, rsyslog/syslog-ng config, and update corresponding mdsd config XML
        try:
            syslogEvents_setting = self._ext_settings.get_syslogEvents_setting()
            fileLogs_setting = self._ext_settings.get_fileLogs_setting()
            lad_logging_config_helper = LadLoggingConfig(syslogEvents_setting, fileLogs_setting, self._sink_configs,
                                                         self._pkey_path, self._cert_path, self._encrypt_secret)
            mdsd_syslog_config = lad_logging_config_helper.get_mdsd_syslog_config()
            mdsd_filelog_config = lad_logging_config_helper.get_mdsd_filelog_config()
            copy_source_mdsdevent_eh_url_elems(self._mdsd_config_xml_tree, mdsd_syslog_config)
            copy_source_mdsdevent_eh_url_elems(self._mdsd_config_xml_tree, mdsd_filelog_config)
            self._fluentd_syslog_src_config = lad_logging_config_helper.get_fluentd_syslog_src_config()
            self._fluentd_tail_src_config = lad_logging_config_helper.get_fluentd_filelog_src_config()
            self._fluentd_out_mdsd_config = lad_logging_config_helper.get_fluentd_out_mdsd_config()
            self._rsyslog_config = lad_logging_config_helper.get_rsyslog_config()
            self._syslog_ng_config = lad_logging_config_helper.get_syslog_ng_config()
        except Exception as e:
            self._logger_error("Failed to create omsagent (fluentd), rsyslog/syslog-ng configs or to update "
                               "corresponding mdsd config XML. Error: {0}\nStacktrace: {1}"
                               .format(e, traceback.format_exc()))
            return False, 'Failed to generate configs for fluentd, syslog, and mdsd ' \
                          '(see extension error logs for more details)'

        # 5. Before starting to update the storage account settings, log extension's entire settings
        #    with secrets redacted, for diagnostic purpose.
        self._ext_settings.log_ext_settings_with_secrets_redacted(self._logger_log, self._logger_error)

        # 6. Actually update the storage account settings on mdsd config XML tree (based on extension's
        #    protectedSettings).
        account = self._ext_settings.read_protected_config('storageAccountName').strip()
        if not account:
            return False, "Must specify storageAccountName"
        if self._ext_settings.read_protected_config('storageAccountKey'):
            return False, "The storageAccountKey protected setting is not supported and must not be used"
        token = self._ext_settings.read_protected_config('storageAccountSasToken').strip()
        if not token or token == '?':
            return False, "Must specify storageAccountSasToken"
        if '?' == token[0]:
            token = token[1:]
        endpoint = get_storage_endpoint_with_account(account,
                                                     self._ext_settings.read_protected_config('storageAccountEndPoint'))
        self._update_account_settings(account, token, endpoint)

        # 7. Update mdsd config XML's eventVolume attribute based on the logic specified in the helper.
        self._set_event_volume(lad_cfg)

        # 8. Finally generate mdsd config XML file out of the constructed XML tree object.
        self._mdsd_config_xml_tree.write(os.path.join(self._ext_dir, 'xmlCfg.xml'))

        return True, ""
    def generate_all_configs(self):
        """
        Generates configs for all components required by LAD.
        Generates XML cfg file for mdsd, from JSON config settings (public & private).
        Also generates rsyslog/syslog-ng configs corresponding to 'syslogEvents' or 'syslogCfg' setting.
        Also generates fluentd's syslog/tail src configs and out_mdsd configs.
        The rsyslog/syslog-ng and fluentd configs are not yet saved to files. They are available through
        the corresponding getter methods of this class (get_fluentd_*_config(), get_*syslog*_config()).

        Returns (True, '') if config was valid and proper xmlCfg.xml was generated.
        Returns (False, '...') if config was invalid and the error message.
        """

        # 1. Add DeploymentId (if available) to identity columns
        if self._deployment_id:
            XmlUtil.setXmlValue(self._mdsd_config_xml_tree, "Management/Identity/IdentityComponent", "",
                                self._deployment_id, ["name", "DeploymentId"])
        # 2. Use ladCfg to generate OMIQuery and LADQuery elements
        lad_cfg = self._ladCfg()
        if lad_cfg:
            try:
                self._update_metric_collection_settings(lad_cfg)
                resource_id = self._ext_settings.get_resource_id()
                if resource_id:
                    XmlUtil.setXmlValue(self._mdsd_config_xml_tree, 'Events/DerivedEvents/DerivedEvent/LADQuery',
                                        'partitionKey', escape_nonalphanumerics(resource_id))
                    lad_query_instance_id = ""
                    uuid_for_instance_id = self._fetch_uuid()
                    if resource_id.find("providers/Microsoft.Compute/virtualMachineScaleSets") >= 0:
                        lad_query_instance_id = uuid_for_instance_id
                    self._set_xml_attr("instanceID", lad_query_instance_id, "Events/DerivedEvents/DerivedEvent/LADQuery")
                    # Set JsonBlob sink-related elements
                    self._add_obo_field(name='resourceId', value=resource_id)
                    self._add_obo_field(name='agentIdentityHash', value=uuid_for_instance_id)

            except Exception as e:
                self._logger_error("Failed to create portal config  error:{0} {1}".format(e, traceback.format_exc()))
                return False, 'Failed to create portal config from ladCfg (see extension error logs for more details)'

        # 3. Generate config for perfCfg. Need to distinguish between non-AppInsights scenario and AppInsights scenario,
        #    so check if Application Insights key is present and pass it to the actual helper
        #    function (self._apply_perf_cfg()).
        try:
            self._apply_perf_cfg()
        except Exception as e:
            self._logger_error("Failed check for Application Insights key in LAD configuration with exception:{0}\n"
                               "Stacktrace: {1}".format(e, traceback.format_exc()))
            return False, 'Failed to update perf counter config (see extension error logs for more details)'

        # 4. Generate omsagent (fluentd) configs, rsyslog/syslog-ng config, and update corresponding mdsd config XML
        try:
            syslogEvents_setting = self._ext_settings.get_syslogEvents_setting()
            fileLogs_setting = self._ext_settings.get_fileLogs_setting()
            lad_logging_config_helper = LadLoggingConfig(syslogEvents_setting, fileLogs_setting, self._sink_configs,
                                                         self._pkey_path, self._cert_path, self._encrypt_secret)
            mdsd_syslog_config = lad_logging_config_helper.get_mdsd_syslog_config(self._ext_settings.read_protected_config('disableStorageAccount') == True)
            mdsd_filelog_config = lad_logging_config_helper.get_mdsd_filelog_config()
            copy_source_mdsdevent_eh_url_elems(self._mdsd_config_xml_tree, mdsd_syslog_config)
            copy_source_mdsdevent_eh_url_elems(self._mdsd_config_xml_tree, mdsd_filelog_config)
            self._fluentd_syslog_src_config = lad_logging_config_helper.get_fluentd_syslog_src_config()
            self._fluentd_tail_src_config = lad_logging_config_helper.get_fluentd_filelog_src_config()
            self._fluentd_out_mdsd_config = lad_logging_config_helper.get_fluentd_out_mdsd_config()
            self._rsyslog_config = lad_logging_config_helper.get_rsyslog_config()
            self._syslog_ng_config = lad_logging_config_helper.get_syslog_ng_config()
        except Exception as e:
            self._logger_error("Failed to create omsagent (fluentd), rsyslog/syslog-ng configs or to update "
                               "corresponding mdsd config XML. Error: {0}\nStacktrace: {1}"
                               .format(e, traceback.format_exc()))
            return False, 'Failed to generate configs for fluentd, syslog, and mdsd ' \
                          '(see extension error logs for more details)'

        # 5. Before starting to update the storage account settings, log extension's entire settings
        #    with secrets redacted, for diagnostic purpose.
        self._ext_settings.log_ext_settings_with_secrets_redacted(self._logger_log, self._logger_error)

        # 6. Actually update the storage account settings on mdsd config XML tree (based on extension's
        #    protectedSettings).
        account = self._ext_settings.read_protected_config('storageAccountName').strip()
        if not account:
            return False, "Must specify storageAccountName"
        if self._ext_settings.read_protected_config('storageAccountKey'):
            return False, "The storageAccountKey protected setting is not supported and must not be used"
        token = self._ext_settings.read_protected_config('storageAccountSasToken').strip()
        if not token or token == '?':
            return False, "Must specify storageAccountSasToken"
        if '?' == token[0]:
            token = token[1:]
        endpoints = get_storage_endpoints_with_account(account,
                                                     self._ext_settings.read_protected_config('storageAccountEndPoint'))
        self._update_account_settings(account, token, endpoints)

        # 7. Update mdsd config XML's eventVolume attribute based on the logic specified in the helper.
        self._set_event_volume(lad_cfg)

        # 8. Finally generate mdsd config XML file out of the constructed XML tree object.
        self._mdsd_config_xml_tree.write(os.path.join(self._ext_dir, 'xmlCfg.xml'))

        return True, ""
def configSettings():
    """
    Generates XML cfg file for mdsd, from JSON config settings (public & private).
    Returns (True, '') if config was valid and proper xmlCfg.xml was generated.
    Returns (False, '...') if config was invalid and the error message.
    """
    mdsdCfgstr = readPublicConfig('mdsdCfg')
    if not mdsdCfgstr:
        with open(os.path.join(WorkDir, './mdsdConfig.xml.template'), "r") as defaulCfg:
            mdsdCfgstr = defaulCfg.read()
    else:
        mdsdCfgstr = base64.b64decode(mdsdCfgstr)
    mdsdCfg = ET.ElementTree()
    mdsdCfg._setroot(XmlUtil.createElement(mdsdCfgstr))

    # Add DeploymentId (if available) to identity columns
    deployment_id = get_deployment_id()
    if deployment_id:
        XmlUtil.setXmlValue(mdsdCfg, "Management/Identity/IdentityComponent", "", deployment_id,
                            ["name", "DeploymentId"])

    try:
        resourceId = getResourceId()
        if resourceId:
            createPortalSettings(mdsdCfg, escape_nonalphanumerics(resourceId))
            instanceID = ""
            if resourceId.find("providers/Microsoft.Compute/virtualMachineScaleSets") >= 0:
                instanceID = read_uuid(waagent.RunGetOutput)
            config(mdsdCfg, "instanceID", instanceID, "Events/DerivedEvents/DerivedEvent/LADQuery")

    except Exception as e:
        hutil.error("Failed to create portal config  error:{0} {1}".format(e, traceback.format_exc()))

    # Check if Application Insights key is present in ladCfg
    ladCfg = readPublicConfig('ladCfg')
    do_ai = False
    aikey = None
    try:
        aikey = AIUtil.tryGetAiKey(ladCfg)
        if aikey:
            hutil.log("Application Insights key found.")
            do_ai = True
        else:
            hutil.log("Application Insights key not found.")
    except Exception as e:
        msg = "Failed check for Application Insights key in LAD configuration with exception:{0} {1}"
        hutil.error(msg.format(e, traceback.format_exc()))

    generatePerformanceCounterConfiguration(mdsdCfg, do_ai)

    syslogCfg = getSyslogCfg()
    fileCfg = getFileCfg()
    # fileCfg = [{"file":"/var/log/waagent.log","table":"waagent"},{"file":"/var/log/waagent2.log","table":"waagent3"}]
    try:
        if fileCfg:
            syslogCfg = createEventFileSettings(mdsdCfg, fileCfg) + syslogCfg

        with open(omfileconfig, 'w') as hfile:
            hfile.write(syslogCfg)
    except Exception as e:
        hutil.error("Failed to create syslog_file config  error:{0} {1}".format(e, traceback.format_exc()))

    log_private_settings_keys(private_settings, hutil.log, hutil.error)

    account = readPrivateConfig('storageAccountName')
    if not account:
        return False, "Empty storageAccountName"
    key = readPrivateConfig('storageAccountKey')
    token = readPrivateConfig('storageAccountSasToken')
    if not key and not token:
        return False, "Neither storageAccountKey nor storageAccountSasToken is given"
    if key and token:
        return False, "Either storageAccountKey or storageAccountSasToken (but not both) should be given"
    endpoint = get_storage_endpoint_with_account(account, readPrivateConfig('storageAccountEndPoint'))

    createAccountSettings(mdsdCfg, account, key, token, endpoint, aikey)

    # Check and add new syslog RouteEvent for Application Insights.
    if aikey:
        AIUtil.createSyslogRouteEventElement(mdsdCfg)

    setEventVolume(mdsdCfg, ladCfg)

    config(mdsdCfg, "sampleRateInSeconds", "60", "Events/OMI/OMIQuery")

    mdsdCfg.write(os.path.join(WorkDir, './xmlCfg.xml'))

    return True, ""
def createAccountSettings(tree, account, key, token, endpoint, aikey=None):
    """
    Update the MDSD configuration Account element with Azure table storage properties.
    Exactly one of (key, token) must be provided. If an aikey is passed, then add a new Account element for Application
    Insights with the application insights key.
    :param tree: The XML doc to be updated.
    :param account: Storage account to which LAD should write data
    :param key: Shared key secret for the storage account, if present
    :param token: SAS token to access the storage account, if present
    :param endpoint: Identifies the Azure instance (public or specific sovereign cloud) where the storage account is
    :param aikey: Key for accessing AI, if present
    """
    assert key or token, "Either key or token must be given."

    def get_handler_cert_pkey_paths():
        handler_settings = hutil.get_handler_settings()
        thumbprint = handler_settings['protectedSettingsCertThumbprint']
        cert_path = waagent.LibDir + '/' + thumbprint + '.crt'
        pkey_path = waagent.LibDir + '/' + thumbprint + '.prv'
        return cert_path, pkey_path

    def encrypt_secret_with_cert(cert_path, secret):
        encrypted_secret_tmp_file_path = os.path.join(WorkDir, "mdsd_secret.bin")
        cmd = "echo -n '{0}' | openssl smime -encrypt -outform DER -out {1} {2}"
        cmd_to_run = cmd.format(secret, encrypted_secret_tmp_file_path, cert_path)
        ret_status, ret_msg = RunGetOutput(cmd_to_run, should_log=False)
        if ret_status is not 0:
            hutil.error("Encrypting storage secret failed with the following message: " + ret_msg)
            return None
        with open(encrypted_secret_tmp_file_path, 'rb') as f:
            encrypted_secret = f.read()
        os.remove(encrypted_secret_tmp_file_path)
        return binascii.b2a_hex(encrypted_secret).upper()

    handler_cert_path, handler_pkey_path = get_handler_cert_pkey_paths()
    if key:
        key = encrypt_secret_with_cert(handler_cert_path, key)
        XmlUtil.setXmlValue(tree, 'Accounts/Account', "account", account, ['isDefault', 'true'])
        XmlUtil.setXmlValue(tree, 'Accounts/Account', "key", key, ['isDefault', 'true'])
        XmlUtil.setXmlValue(tree, 'Accounts/Account', "decryptKeyPath", handler_pkey_path, ['isDefault', 'true'])
        XmlUtil.setXmlValue(tree, 'Accounts/Account', "tableEndpoint", endpoint, ['isDefault', 'true'])
        XmlUtil.removeElement(tree, 'Accounts', 'SharedAccessSignature')
    else:  # token
        token = encrypt_secret_with_cert(handler_cert_path, token)
        XmlUtil.setXmlValue(tree, 'Accounts/SharedAccessSignature', "account", account, ['isDefault', 'true'])
        XmlUtil.setXmlValue(tree, 'Accounts/SharedAccessSignature', "key", token, ['isDefault', 'true'])
        XmlUtil.setXmlValue(tree, 'Accounts/SharedAccessSignature', "decryptKeyPath", handler_pkey_path,
                            ['isDefault', 'true'])
        XmlUtil.setXmlValue(tree, 'Accounts/SharedAccessSignature', "tableEndpoint", endpoint, ['isDefault', 'true'])
        XmlUtil.removeElement(tree, 'Accounts', 'Account')

    if aikey:
        AIUtil.createAccountElement(tree, aikey)
    def generate_mdsd_rsyslog_configs(self):
        """
        Generates XML cfg file for mdsd, from JSON config settings (public & private).
        Also generates rsyslog imfile conf file from the 'fileCfg' setting.
        Returns (True, '') if config was valid and proper xmlCfg.xml was generated.
        Returns (False, '...') if config was invalid and the error message.
        """

        # 1. Get the mdsd config XML tree base.
        #    - 1st priority is from the extension setting's 'mdsdCfg' value.
        #      Note that we have never used this option.
        #    - 2nd priority is to use the provided XML template stored in <ext_dir>/mdsdConfig.xml.template.
        mdsd_cfg_str = self._ext_settings.get_mdsd_cfg()
        if not mdsd_cfg_str:
            with open(os.path.join(self._ext_dir, './mdsdConfig.xml.template'), "r") as f:
                mdsd_cfg_str = f.read()
        self._mdsd_config_xml_tree = ET.ElementTree()
        self._mdsd_config_xml_tree._setroot(XmlUtil.createElement(mdsd_cfg_str))

        # 2. Add DeploymentId (if available) to identity columns
        if self._deployment_id:
            XmlUtil.setXmlValue(self._mdsd_config_xml_tree, "Management/Identity/IdentityComponent", "",
                                self._deployment_id, ["name", "DeploymentId"])
        # 2.1. Apply resourceId attribute to LADQuery elements
        try:
            resource_id = self._ext_settings.get_resource_id()
            if resource_id:
                escaped_resource_id_str = escape_nonalphanumerics(resource_id)
                self._add_portal_settings(escaped_resource_id_str)
                instanceID = ""
                if resource_id.find("providers/Microsoft.Compute/virtualMachineScaleSets") >= 0:
                    instanceID = read_uuid(self._run_command)
                self._set_xml_attr("instanceID", instanceID, "Events/DerivedEvents/DerivedEvent/LADQuery")
        except Exception as e:
            self._logger_error("Failed to create portal config  error:{0} {1}".format(e, traceback.format_exc()))

        # 3. Update perf counter config. Need to distinguish between non-AppInsights scenario and AppInsights scenario,
        #    so check if Application Insights key is present in ladCfg first, and pass it to the actual helper
        #    function (self._apply_perf_cfgs()).
        lad_cfg = self._ext_settings.read_public_config('ladCfg')
        do_ai = False
        aikey = None
        try:
            aikey = AIUtil.tryGetAiKey(lad_cfg)
            if aikey:
                self._logger_log("Application Insights key found.")
                do_ai = True
            else:
                self._logger_log("Application Insights key not found.")
        except Exception as e:
            self._logger_error("Failed check for Application Insights key in LAD configuration with exception:{0}\n"
                               "Stacktrace: {1}".format(e, traceback.format_exc()))
        self._apply_perf_cfgs(do_ai)

        # 4. Generate rsyslog imfile config. It's unclear why non-imfile config stuff (self._get_syslog_config())
        #    is appended to imfileconfig as well. That part has never been used, as far as I remember, and will
        #    definitely need to change later.
        syslog_cfg = self._ext_settings.get_syslog_config()
        file_cfg = self._ext_settings.get_file_monitoring_config()
        # fileCfg = [{"file":"/var/log/waagent.log","table":"waagent"},{"file":"/var/log/waagent2.log","table":"waagent3"}]
        try:
            if file_cfg:
                syslog_cfg = self._update_and_get_file_monitoring_settings(file_cfg) + syslog_cfg
            with open(self._imfile_config_filename, 'w') as imfile_config_file:
                imfile_config_file.write(syslog_cfg)
        except Exception as e:
            self._logger_error("Failed to create rsyslog imfile config. Error:{0}\n"
                         "Stacktrace: {1}".format(e, traceback.format_exc()))

        # 5. Before starting to update the storage account settings, log extension's protected settings'
        #    keys only (except well-known values), for diagnostic purpose. This is mainly to make sure that
        #    the extension's Json settings include a correctly entered 'storageEndpoint'.
        self._ext_settings.log_protected_settings_keys(self._logger_log, self._logger_error)

        # 6. Actually update the storage account settings on mdsd config XML tree (based on extension's
        #    protectedSettings).
        account = self._ext_settings.read_protected_config('storageAccountName')
        if not account:
            return False, "Empty storageAccountName"
        key = self._ext_settings.read_protected_config('storageAccountKey')
        token = self._ext_settings.read_protected_config('storageAccountSasToken')
        if not key and not token:
            return False, "Neither storageAccountKey nor storageAccountSasToken is given"
        if key and token:
            return False, "Either storageAccountKey or storageAccountSasToken (but not both) should be given"
        endpoint = get_storage_endpoint_with_account(account,
                                                     self._ext_settings.read_protected_config('storageAccountEndPoint'))
        self._update_account_settings(account, key, token, endpoint, aikey)

        # 7. Check and add new syslog RouteEvent for Application Insights.
        if aikey:
            AIUtil.createSyslogRouteEventElement(self._mdsd_config_xml_tree)

        # 8. Update mdsd config XML's eventVolume attribute based on the logic specified in the helper.
        self._set_event_volume(lad_cfg)

        # 9. Update mdsd config XML's sampleRateInSeconds attribute with default '60'
        self._set_xml_attr("sampleRateInSeconds", "60", "Events/OMI/OMIQuery")

        # 10. Finally generate mdsd config XML file out of the constructed XML tree object.
        self._mdsd_config_xml_tree.write(os.path.join(self._ext_dir, './xmlCfg.xml'))

        return True, ""