def get_imds_values(is_lad): """ Query imds to get required values for MetricsExtension config for this VM """ retries = 1 max_retries = 3 sleep_time = 5 imdsurl = "" is_arc = False if is_lad: imdsurl = "http://169.254.169.254/metadata/instance?api-version=2019-03-11" else: if metrics_utils.is_arc_installed(): imdsurl = metrics_utils.get_arc_endpoint() imdsurl += "/metadata/instance?api-version=2019-11-01" is_arc = True else: imdsurl = "http://169.254.169.254/metadata/instance?api-version=2019-03-11" data = None while retries <= max_retries: #query imds to get the required information req = urllib.request.Request(imdsurl, headers={'Metadata': 'true'}) res = urllib.request.urlopen(req) data = json.loads(res.read().decode('utf-8', 'ignore')) if "compute" not in data: retries += 1 else: break time.sleep(sleep_time) if retries > max_retries: raise Exception( "Unable to find 'compute' key in imds query response. Reached max retry limit of - {0} times. Failed to setup ME." .format(max_retries)) return False if "resourceId" not in data["compute"]: raise Exception( "Unable to find 'resourceId' key in imds query response. Failed to setup ME." ) return False az_resource_id = data["compute"]["resourceId"] if "subscriptionId" not in data["compute"]: raise Exception( "Unable to find 'subscriptionId' key in imds query response. Failed to setup ME." ) return False subscription_id = data["compute"]["subscriptionId"] if "location" not in data["compute"]: raise Exception( "Unable to find 'location' key in imds query response. Failed to setup ME." ) return False location = data["compute"]["location"] return az_resource_id, subscription_id, location, data
def handle_config(config_data, me_url, mdsd_url, is_lad): """ The main method to perfom the task of parsing the config , writing them to disk, setting up, stopping, removing and starting telegraf :param config_data: Parsed Metrics Configuration from which telegraf config is created :param me_url: The url to which telegraf will send metrics to for MetricsExtension :param mdsd_url: The url to which telegraf will send metrics to for MDSD :param is_lad: Boolean value for whether the extension is Lad or not (AMA) """ # Making the imds call to get resource id, sub id, resource group and region for the dimensions for telegraf metrics retries = 1 max_retries = 3 sleep_time = 5 imdsurl = "" is_arc = False if is_lad: imdsurl = "http://169.254.169.254/metadata/instance?api-version=2019-03-11" else: if metrics_utils.is_arc_installed(): imdsurl = metrics_utils.get_arc_endpoint() imdsurl += "/metadata/instance?api-version=2019-11-01" is_arc = True else: imdsurl = "http://169.254.169.254/metadata/instance?api-version=2019-03-11" data = None while retries <= max_retries: req = urllib.request.Request(imdsurl, headers={'Metadata': 'true'}) res = urllib.request.urlopen(req) data = json.loads(res.read().decode('utf-8', 'ignore')) if "compute" not in data: retries += 1 else: break time.sleep(sleep_time) if retries > max_retries: raise Exception( "Unable to find 'compute' key in imds query response. Reached max retry limit of - {0} times. Failed to setup Telegraf." .format(max_retries)) if "resourceId" not in data["compute"]: raise Exception( "Unable to find 'resourceId' key in imds query response. Failed to setup Telegraf." ) az_resource_id = data["compute"]["resourceId"] # If the instance is VMSS then trim the last two values from the resource id ie - "/virtualMachines/0" # Since ME expects the resource id in a particular format. For egs - # IMDS returned ID - /subscriptions/<sub-id>/resourceGroups/<rg_name>/providers/Microsoft.Compute/virtualMachineScaleSets/<VMSSName>/virtualMachines/0 # ME expected ID- /subscriptions/<sub-id>/resourceGroups/<rg_name>/providers/Microsoft.Compute/virtualMachineScaleSets/<VMSSName> if "virtualMachineScaleSets" in az_resource_id: az_resource_id = "/".join(az_resource_id.split("/")[:-2]) if "subscriptionId" not in data["compute"]: raise Exception( "Unable to find 'subscriptionId' key in imds query response. Failed to setup Telegraf." ) subscription_id = data["compute"]["subscriptionId"] if "resourceGroupName" not in data["compute"]: raise Exception( "Unable to find 'resourceGroupName' key in imds query response. Failed to setup Telegraf." ) resource_group = data["compute"]["resourceGroupName"] if "location" not in data["compute"]: raise Exception( "Unable to find 'location' key in imds query response. Failed to setup Telegraf." ) region = data["compute"]["location"] virtual_machine_name = "" if "vmScaleSetName" in data[ "compute"] and data["compute"]["vmScaleSetName"] != "": virtual_machine_name = data["compute"]["name"] #call the method to first parse the configs output, namespaces = parse_config(config_data, me_url, mdsd_url, is_lad, az_resource_id, subscription_id, resource_group, region, virtual_machine_name) _, configFolder = get_handler_vars() if is_lad: telegraf_bin = metrics_constants.lad_telegraf_bin else: telegraf_bin = metrics_constants.ama_telegraf_bin telegraf_conf_dir = configFolder + "/telegraf_configs/" telegraf_agent_conf = telegraf_conf_dir + "telegraf.conf" telegraf_d_conf_dir = telegraf_conf_dir + "telegraf.d/" #call the method to write the configs write_configs(output, telegraf_conf_dir, telegraf_d_conf_dir) # Setup Telegraf service. # If the VM has systemd, then we will copy over the systemd unit file and use that to start/stop if metrics_utils.is_systemd(): telegraf_service_setup = setup_telegraf_service( telegraf_bin, telegraf_d_conf_dir, telegraf_agent_conf) if not telegraf_service_setup: return False, [] return True, namespaces
def generate_MSI_token(): """ This method is used to query the metdadata service to get the MSI Auth token for the VM and write it to the ME config location This is called from the main extension code after config setup is complete """ if metrics_utils.is_arc_installed(): return generate_Arc_MSI_token() else: _, configFolder = get_handler_vars() me_config_dir = configFolder + "/metrics_configs/" me_auth_file_path = me_config_dir + "AuthToken-MSI.json" expiry_epoch_time = "" log_messages = "" retries = 1 max_retries = 3 sleep_time = 5 if not os.path.exists(me_config_dir): log_messages += "Metrics extension config directory - {0} does not exist. Failed to generate MSI auth token fo ME.\n".format( me_config_dir) return False, expiry_epoch_time, log_messages try: data = None while retries <= max_retries: msiauthurl = "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://ingestion.monitor.azure.com/" req = urllib.request.Request(msiauthurl, headers={ 'Metadata': 'true', 'Content-Type': 'application/json' }) res = urllib.request.urlopen(req) data = json.loads(res.read().decode('utf-8', 'ignore')) if not data or "access_token" not in data: retries += 1 else: break log_messages += "Failed to fetch MSI Auth url. Retrying in {2} seconds. Retry Count - {0} out of Mmax Retries - {1}\n".format( retries, max_retries, sleep_time) time.sleep(sleep_time) if retries > max_retries: log_messages += "Unable to generate a valid MSI auth token at {0}.\n".format( me_auth_file_path) return False, expiry_epoch_time, log_messages with open(me_auth_file_path, "w") as f: f.write(json.dumps(data)) if "expires_on" in data: expiry_epoch_time = data["expires_on"] else: log_messages += "Error parsing the msi token at {0} for the token expiry time. Failed to generate the correct token\n".format( me_auth_file_path) return False, expiry_epoch_time, log_messages except Exception as e: log_messages += "Failed to get msi auth token. Please check if VM's system assigned Identity is enabled Failed with error {0}\n".format( e) return False, expiry_epoch_time, log_messages return True, expiry_epoch_time, log_messages