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)
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 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 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, ""