#!/usr/bin/env python
#
# DSC extension
#
# Copyright 2015 Microsoft Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os
import os.path
import re
import subprocess
import sys
import traceback
try:
    from urllib.parse import urlparse, urlencode
    from urllib.request import urlopen, Request
    from urllib.error import HTTPError
except ImportError:
    from urlparse import urlparse
    from urllib import urlencode
    from urllib2 import urlopen, Request, HTTPError
import time
import platform
import json
import datetime
import serializerfactory
import httpclient
import urllib2httpclient
import urllib3httpclient
import httpclientfactory

from azure.storage import BlobService
from Utils.WAAgentUtil import waagent
import Utils.HandlerUtil as Util

# Define global variables

ExtensionName = 'Microsoft.OSTCExtensions.DSCForLinux'
ExtensionShortName = 'DSCForLinux'
DownloadDirectory = 'download'

omi_package_prefix = 'packages/omi-1.7.3-0.ssl_'
dsc_package_prefix = 'packages/dsc-1.2.4-0.ssl_'
omi_major_version = 1
omi_minor_version = 7
omi_build = 3
omi_release = 0
dsc_major_version = 1
dsc_minor_version = 2
dsc_build = 4
dsc_release = 0
package_pattern = '(\d+).(\d+).(\d+).(\d+)'
nodeid_path = '/etc/opt/omi/conf/dsc/agentid'
date_time_format = "%Y-%m-%dT%H:%M:%SZ"
extension_handler_version = "3.0.0.6"
python_command = 'python3' if sys.version_info >= (3,0) else 'python'
dsc_script_path = '/opt/microsoft/dsc/Scripts/python3' if sys.version_info >= (3,0) else '/opt/microsoft/dsc/Scripts'
space_string = " "

# Error codes
UnsupportedDistro = 51 #excludes from SLA
DPKGLockedErrorCode = 51 #excludes from SLA

# DSC-specific Operation
class Operation:
    Download = "Download"
    ApplyMof = "ApplyMof"
    ApplyMetaMof = "ApplyMetaMof"
    InstallModule = "InstallModule"
    RemoveModule = "RemoveModule"
    Register = "Register"
    Enable = "Enable"


class DistroCategory:
    debian = 1
    redhat = 2
    suse = 3


class Mode:
    push = "push"
    pull = "pull"
    install = "install"
    remove = "remove"
    register = "register"


def main():
    waagent.LoggerInit('/var/log/waagent.log', '/dev/stdout')
    waagent.Log("%s started to handle." % (ExtensionShortName))

    global hutil
    hutil = Util.HandlerUtility(waagent.Log, waagent.Error)
    hutil.try_parse_context()

    global public_settings
    public_settings = hutil.get_public_settings()
    if not public_settings:
        waagent.AddExtensionEvent(name=ExtensionShortName, op='MainInProgress', isSuccess=True,
                                  message="Public settings are NOT provided.")
        public_settings = {}

    global protected_settings
    protected_settings = hutil.get_protected_settings()
    if not protected_settings:
        waagent.AddExtensionEvent(name=ExtensionShortName, op='MainInProgress', isSuccess=True,
                                  message="protected settings are NOT provided.")
        protected_settings = {}

    global distro_category
    vm_supported, vm_dist, vm_ver = check_supported_OS()
    distro_category = get_distro_category(vm_dist.lower(), vm_ver.lower())
    

    for a in sys.argv[1:]:
        if re.match("^([-/]*)(disable)", a):
            disable()
        elif re.match("^([-/]*)(uninstall)", a):
            uninstall()
        elif re.match("^([-/]*)(install)", a):
            install()
        elif re.match("^([-/]*)(enable)", a):
            enable()
        elif re.match("^([-/]*)(update)", a):
            update()


def get_distro_category(distro_name,distro_version):
    if distro_name.startswith('ubuntu') or (distro_name.startswith('debian')):
        return DistroCategory.debian
    elif distro_name.startswith('centos') or distro_name.startswith('redhat') or distro_name.startswith('oracle') or distro_name.startswith('red hat'):
        return DistroCategory.redhat
    elif distro_name.startswith('suse') or distro_name.startswith('sles'):
        return DistroCategory.suse 
    waagent.AddExtensionEvent(name=ExtensionShortName, op='InstallInProgress', isSuccess=True, message="Unsupported distro :" + distro_name + "; distro_version: " + distro_version)
    hutil.do_exit(UnsupportedDistro, 'Install', 'error', str(UnsupportedDistro), distro_name + 'is not supported.')
    
def check_supported_OS():
    """
    Checks if the VM this extension is running on is supported by DSC
    Returns for platform.linux_distribution() vary widely in format, such as
    '7.3.1611' returned for a VM with CentOS 7, so the first provided
    digits must match.
    All other distros not supported will get error code 51
    """
    supported_dists = {'redhat' : ['7', '8'], # CentOS
                       'centos' : ['7', '8'], # CentOS
                       'red hat' : ['7', '8'], # Redhat
                       'debian' : ['8', '9', '10'], # Debian
                       'ubuntu' : ['14.04', '16.04', '18.04', '20.04'], # Ubuntu
                       'oracle' : ['7'], # Oracle
                       'suse' : ['12', '15'], #SLES
                       'sles' : ['12', '15']
    }
    vm_dist, vm_ver, vm_supported = '', '', False
    
    try:
        vm_dist, vm_ver, vm_id = platform.linux_distribution()
    except AttributeError:
        try:
            vm_dist, vm_ver, vm_id = platform.dist()
        except:
            waagent.Log("Falling back to /etc/os-release distribution parsing")

    # Fallback if either of the above fail; on some (especially newer)
    # distros, linux_distribution() and dist() are unreliable or deprecated
    if not vm_dist and not vm_ver:
        try:
            with open('/etc/os-release', 'r') as fp:
                for line in fp:
                    if line.startswith('ID='):
                        vm_dist = line.split('=')[1]
                        vm_dist = vm_dist.split('-')[0]
                        vm_dist = vm_dist.replace('\"', '').replace('\n', '')
                    elif line.startswith('VERSION_ID='):
                        vm_ver = line.split('=')[1]
                        vm_ver = vm_ver.replace('\"', '').replace('\n', '')
        except:
            waagent.Log('Indeterminate operating system')
            return vm_supported, 'Indeterminate operating system', ''

    # Find this VM distribution in the supported list
    for supported_dist in supported_dists.keys():
        if vm_dist.lower().startswith(supported_dist):
            # Check if this VM distribution version is supported
            vm_ver_split = vm_ver.split('.')
            for supported_ver in supported_dists[supported_dist]:
                supported_ver_split = supported_ver.split('.')

                # If vm_ver is at least as precise (at least as many digits) as
                # supported_ver and matches all the supported_ver digits, then
                # this VM is supported
                vm_ver_match = True
                for idx, supported_ver_num in enumerate(supported_ver_split):
                    try:
                        supported_ver_num = int(supported_ver_num)
                        vm_ver_num = int(vm_ver_split[idx])
                    except IndexError:
                        vm_ver_match = False
                        break
                    if vm_ver_num is not supported_ver_num:
                        vm_ver_match = False
                        break
                if vm_ver_match:
                    vm_supported = True
                    break

    if not vm_supported:
        waagent.AddExtensionEvent(name=ExtensionShortName, op='InstallInProgress', isSuccess=True, message="Unsupported OS :" + vm_dist + "; distro_version: " + vm_ver)
        hutil.do_exit(UnsupportedDistro, 'Install', 'error', str(UnsupportedDistro), vm_dist + "; distro_version: " + vm_ver + ' is not supported.')
    
    return vm_supported, vm_dist, vm_ver


def install():
    hutil.do_parse_context('Install')
    try:
        waagent.AddExtensionEvent(name=ExtensionShortName, op='InstallInProgress', isSuccess=True,
                                  message="Installing DSCForLinux extension")
        remove_old_dsc_packages()
        install_dsc_packages()
        waagent.AddExtensionEvent(name=ExtensionShortName, op='InstallInProgress', isSuccess=True,
                                  message="successfully installed DSCForLinux extension")
        hutil.do_exit(0, 'Install', 'success', '0', 'Install Succeeded.')
    except Exception as e:
        waagent.AddExtensionEvent(name=ExtensionShortName, op='InstallInProgress', isSuccess=True,
                                  message="failed to install DSC extension with error: {0} and stacktrace: {1}".format(
                                      str(e), traceback.format_exc()))
        hutil.error(
            "Failed to install DSC extension with error: %s, stack trace: %s" % (str(e), traceback.format_exc()))
        hutil.do_exit(1, 'Install', 'error', '1', 'Install Failed.')


def enable():
    hutil.do_parse_context('Enable')
    hutil.exit_if_enabled()
    try:
        start_omiservice()
        mode = get_config('Mode')
        if mode == '':
            mode = get_config('ExtensionAction')
        waagent.AddExtensionEvent(name=ExtensionShortName, op='EnableInProgress', isSuccess=True,
                                  message="Enabling the DSC extension - mode/ExtensionAction: " + mode)
        if mode == '':
            mode = Mode.push
        else:
            mode = mode.lower()
            if not hasattr(Mode, mode):
                waagent.AddExtensionEvent(name=ExtensionShortName,
                                          op=Operation.Enable,
                                          isSuccess=True,
                                          message="(03001)Argument error, invalid ExtensionAction/mode.")
                hutil.do_exit(51, 'Enable', 'error', '51', 'Enable failed, unknown ExtensionAction/mode: ' + mode)
        if mode == Mode.remove:
            remove_module()
        elif mode == Mode.register:
            registration_key = get_config('RegistrationKey')
            registation_url = get_config('RegistrationUrl')
            # Optional
            node_configuration_name = get_config('NodeConfigurationName')
            refresh_freq = get_config('RefreshFrequencyMins')
            configuration_mode_freq = get_config('ConfigurationModeFrequencyMins')
            configuration_mode = get_config('ConfigurationMode')
            exit_code, err_msg = register_automation(registration_key, registation_url, node_configuration_name,
                                                     refresh_freq, configuration_mode_freq, configuration_mode.lower())
            if exit_code != 0:
                hutil.do_exit(exit_code, 'Enable', 'error', str(exit_code), err_msg)

            extension_status_event = "ExtensionRegistration"
            response = send_heart_beat_msg_to_agent_service(extension_status_event)
            status_file_path, agent_id, vm_uuid = get_status_message_details()
            update_statusfile(status_file_path, agent_id, vm_uuid, response)
            sys.exit(0)
        else:
            file_path = download_file()
            if mode == Mode.pull:
                current_config = apply_dsc_meta_configuration(file_path)
            elif mode == Mode.push:
                current_config = apply_dsc_configuration(file_path)
            else:
                install_module(file_path)
        if mode == Mode.push or mode == Mode.pull:
            if check_dsc_configuration(current_config):
                if mode == Mode.push:
                    waagent.AddExtensionEvent(name=ExtensionShortName,
                                              op=Operation.ApplyMof,
                                              isSuccess=True,
                                              message="(03104)Succeeded to apply MOF configuration through Push Mode")
                else:
                    waagent.AddExtensionEvent(name=ExtensionShortName,
                                              op=Operation.ApplyMetaMof,
                                              isSuccess=True,
                                              message="(03106)Succeeded to apply meta MOF configuration through Pull Mode")
                    extension_status_event = "ExtensionRegistration"
                    response = send_heart_beat_msg_to_agent_service(extension_status_event)
                    status_file_path, agent_id, vm_uuid = get_status_message_details()
                    update_statusfile(status_file_path, agent_id, vm_uuid, response)
                    sys.exit(0)
            else:
                if mode == Mode.push:
                    waagent.AddExtensionEvent(name=ExtensionShortName,
                                              op=Operation.ApplyMof,
                                              isSuccess=False,
                                              message="(03105)Failed to apply MOF configuration through Push Mode")
                else:
                    waagent.AddExtensionEvent(name=ExtensionShortName,
                                              op=Operation.ApplyMetaMof,
                                              isSuccess=False,
                                              message="(03107)Failed to apply meta MOF configuration through Pull Mode")
                hutil.do_exit(1, 'Enable', 'error', '1', 'Enable failed. ' + current_config)

        hutil.do_exit(0, 'Enable', 'success', '0', 'Enable Succeeded')
    except Exception as e:
        waagent.AddExtensionEvent(name=ExtensionShortName, op='EnableInProgress', isSuccess=True,
                                  message="Enable failed with the error: {0}, stacktrace: {1} ".format(str(e),
                                                                                                       traceback.format_exc()))
        hutil.error('Failed to enable the extension with error: %s, stack trace: %s' % (str(e), traceback.format_exc()))
        hutil.do_exit(1, 'Enable', 'error', '1', 'Enable failed: {0}'.format(e))


def send_heart_beat_msg_to_agent_service(status_event_type):
    response = None
    try:
        retry_count = 0
        canRetry = True
        while retry_count <= 5 and canRetry:
            waagent.AddExtensionEvent(name=ExtensionShortName, op='HeartBeatInProgress', isSuccess=True,
                                      message="In send_heart_beat_msg_to_agent_service method")
            code, output, stderr = run_cmd( python_command + space_string +  dsc_script_path + "/GetDscLocalConfigurationManager.py")
            if code == 0 and "RefreshMode=Pull" in output:
                waagent.AddExtensionEvent(name=ExtensionShortName, op='HeartBeatInProgress', isSuccess=True,
                                          message="sends heartbeat message in pullmode")
                m = re.search("ServerURL=([^\n]+)", output)
                if not m:
                    return
                registration_url = m.group(1)
                agent_id = get_nodeid(nodeid_path)
                node_extended_properties_url = registration_url + "/Nodes(AgentId='" + agent_id + "')/ExtendedProperties"
                waagent.AddExtensionEvent(name=ExtensionShortName, op='HeartBeatInProgress', isSuccess=True,
                                          message="Url is " + node_extended_properties_url)
                headers = {'Content-Type': "application/json; charset=utf-8", 'Accept': "application/json",
                           "ProtocolVersion": "2.0"}
                data = construct_node_extension_properties(output, status_event_type)

                http_client_factory = httpclientfactory.HttpClientFactory("/etc/opt/omi/ssl/oaas.crt",
                                                                          "/etc/opt/omi/ssl/oaas.key")
                http_client = http_client_factory.create_http_client(sys.version_info)

                response = http_client.post(node_extended_properties_url, headers=headers, data=data)
                waagent.AddExtensionEvent(name=ExtensionShortName, op='HeartBeatInProgress', isSuccess=True,
                                          message="response code is " + str(response.status_code))
                if response.status_code >= 500 and response.status_code < 600:
                    canRetry = True
                    time.sleep(10)
                else:
                    canRetry = False
            retry_count += 1
    except Exception as e:
        waagent.AddExtensionEvent(name=ExtensionShortName, op='HeartBeatInProgress', isSuccess=True,
                                  message="Failed to send heartbeat message to DSC agent service: {0}, stacktrace: {1} ".format(
                                      str(e), traceback.format_exc()))
        hutil.error('Failed to send heartbeat message to DSC agent service: %s, stack trace: %s' % (
            str(e), traceback.format_exc()))
    return response


def get_lcm_config_setting(setting_name, lcmconfig):
    valuegroup = re.search(setting_name + "=([^\n]+)", lcmconfig)
    if not valuegroup:
        return ""
    value = valuegroup.group(1)

    return value


def construct_node_extension_properties(lcmconfig, status_event_type):
    waagent.AddExtensionEvent(name=ExtensionShortName, op='HeartBeatInProgress', isSuccess=True,
                              message="Getting properties")
    OMSCLOUD_ID = get_omscloudid()
    
    vm_dist, vm_ver, vm_id = '', '', ''
    
    try:
        vm_dist, vm_ver, vm_id = platform.linux_distribution()
    except AttributeError:
        try:
            vm_dist, vm_ver, vm_id = platform.dist()
        except AttributeError:
            waagent.Log("Falling back to /etc/os-release distribution parsing")

    # Fallback if either of the above fail; on some (especially newer)
    # distros, linux_distribution() and dist() are unreliable or deprecated
    if not vm_dist and not vm_ver:
        try:
            with open('/etc/os-release', 'r') as fp:
                for line in fp:
                    if line.startswith('ID='):
                        vm_dist = line.split('=')[1]
                        vm_dist = vm_dist.split('-')[0]
                        vm_dist = vm_dist.replace('\"', '').replace('\n', '')
                    elif line.startswith('VERSION_ID='):
                        vm_ver = line.split('=')[1]
                        vm_ver = vm_ver.replace('\"', '').replace('\n', '')
        except:
            waagent.Log('Indeterminate operating system')
            vm_dist, vm_ver, vm_id = "Indeterminate operating system", "",""

    if len(vm_ver.split('.')) == 1:
        major_version = vm_ver.split('.')[0]
        minor_version = 0
    if len(vm_ver.split('.')) >= 2:
        major_version = vm_ver.split('.')[0]
        minor_version = vm_ver.split('.')[1]
        
    VMUUID = get_vmuuid()
    node_config_names = get_lcm_config_setting('ConfigurationNames', lcmconfig)
    configuration_mode = get_lcm_config_setting("ConfigurationMode", lcmconfig)
    configuration_mode_frequency = get_lcm_config_setting("ConfigurationModeFrequencyMins", lcmconfig)
    refresh_frequency_mins = get_lcm_config_setting("RefreshFrequencyMins", lcmconfig)
    reboot_node = get_lcm_config_setting("RebootNodeIfNeeded", lcmconfig)
    action_after_reboot = get_lcm_config_setting("ActionAfterReboot", lcmconfig)
    allow_module_overwrite = get_lcm_config_setting("AllowModuleOverwrite", lcmconfig)

    waagent.AddExtensionEvent(name=ExtensionShortName, op='HeartBeatInProgress', isSuccess=True,
                              message="Constructing properties data")

    properties_data = {
        "OMSCloudId": OMSCLOUD_ID,
        "TimeStamp": time.strftime(date_time_format, time.gmtime()),
        "VMResourceId": "",
        "ExtensionStatusEvent": status_event_type,
        "ExtensionInformation": {
            "Name": "Microsoft.OSTCExtensions.DSCForLinux",
            "Version": extension_handler_version
        },
        "OSProfile": {
            "Name": vm_dist,
            "Type": "Linux",
            "MinorVersion": minor_version,
            "MajorVersion": major_version,
            "VMUUID": VMUUID
        },
        "RegistrationMetaData": {
            "NodeConfigurationName": node_config_names,
            "ConfigurationMode": configuration_mode,
            "ConfigurationModeFrequencyMins": configuration_mode_frequency,
            "RefreshFrequencyMins": refresh_frequency_mins,
            "RebootNodeIfNeeded": reboot_node,
            "ActionAfterReboot": action_after_reboot,
            "AllowModuleOverwrite": allow_module_overwrite
        }
    }
    return properties_data


def uninstall():
    hutil.do_parse_context('Uninstall')
    try:
        extension_status_event = "ExtensionUninstall"
        send_heart_beat_msg_to_agent_service(extension_status_event)
        hutil.do_exit(0, 'Uninstall', 'success', '0', 'Uninstall Succeeded')
    except Exception as e:
        waagent.AddExtensionEvent(name=ExtensionShortName, op='UninstallInProgress', isSuccess=False,
                                  message='Failed to uninstall the extension with error: %s, stack trace: %s' % (
                                      str(e), traceback.format_exc()))
        hutil.error(
            'Failed to uninstall the extension with error: %s, stack trace: %s' % (str(e), traceback.format_exc()))
        hutil.do_exit(1, 'Uninstall', 'error', '1', 'Uninstall failed: {0}'.format(e))


def disable():
    hutil.do_parse_context('Disable')
    hutil.do_exit(0, 'Disable', 'success', '0', 'Disable Succeeded')


def update():
    hutil.do_parse_context('Update')
    try:
        extension_status_event = "ExtensionUpgrade"
        send_heart_beat_msg_to_agent_service(extension_status_event)
        hutil.do_exit(0, 'Update', 'success', '0', 'Update Succeeded')
    except Exception as e:
        waagent.AddExtensionEvent(name=ExtensionShortName, op='UpdateInProgress', isSuccess=False,
                                  message='Failed to update the extension with error: %s, stack trace: %s' % (
                                      str(e), traceback.format_exc()))
        hutil.error('Failed to update the extension with error: %s, stack trace: %s' % (str(e), traceback.format_exc()))
        hutil.do_exit(1, 'Update', 'error', '1', 'Update failed: {0}'.format(e))


def run_cmd(cmd):
    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, close_fds=True)
    exit_code = proc.wait()
    stdout, stderr = proc.communicate()
    stdout = stdout.decode("ISO-8859-1") if isinstance(stdout, bytes) else stdout
    stderr = stderr.decode("ISO-8859-1") if isinstance(stderr, bytes) else stderr
    return exit_code, stdout, stderr

def run_dpkg_cmd_with_retry(cmd):
    """
    Attempts to run the cmd - if it fails, checks to see if dpkg is locked by another
    process, if so, it will sleep for 5 seconds and then try running the command again.
    If dpkg is still locked, then it will return the DPKGLockedErrorCode which won't
    count against our SLA numbers.
    """
    exit_code, output, stderr = run_cmd(cmd)
    if not exit_code == 0:
        dpkg_locked = is_dpkg_locked(exit_code, stderr)
        if dpkg_locked:
            # Try one more time:
            time.sleep(5)
            exit_code, output, stderr = run_cmd(cmd)
            dpkg_locked = is_dpkg_locked(exit_code, stderr)
            if dpkg_locked:
                exit_code = DPKGLockedErrorCode

    return exit_code, output, stderr

def get_config(key):
    if key in public_settings:
        value = public_settings.get(key)
        if value:
            return str(value).strip()
    if key in protected_settings:
        value = protected_settings.get(key)
        if value:
            return str(value).strip()
    return ''


def remove_old_dsc_packages():
    waagent.AddExtensionEvent(name=ExtensionShortName, op='InstallInProgress', isSuccess=True,
                              message="Deleting DSC and omi packages")
    if distro_category == DistroCategory.debian:
        deb_remove_incomptible_dsc_package()
        # remove the package installed by Linux DSC 1.0, in later versions the package name is changed to 'omi'
        deb_remove_old_oms_package('omiserver', '1.0.8.2')
    elif distro_category == DistroCategory.redhat or distro_category == DistroCategory.suse:
        rpm_remove_incomptible_dsc_package()
        # remove the package installed by Linux DSC 1.0, in later versions the package name is changed to 'omi'
        rpm_remove_old_oms_package('omiserver', '1.0.8-2')


def deb_remove_incomptible_dsc_package():
    version = deb_get_pkg_version('dsc')
    if version is not None and is_incomptible_dsc_package(version):
        deb_uninstall_package('dsc')


def is_incomptible_dsc_package(package_version):
    version = re.match(package_pattern, package_version)
    # uninstall DSC package if the version is 1.0.x because upgrading from 1.0 to 1.1 is broken
    if version is not None and (int(version.group(1)) == 1 and int(version.group(2)) == 0):
        return True
    return False


def is_old_oms_server(package_name):
    if package_name == 'omiserver':
        return True
    return False


def deb_remove_old_oms_package(package_name, version):
    system_pkg_version = deb_get_pkg_version(package_name)
    if system_pkg_version is not None and is_old_oms_server(package_name):
        deb_uninstall_package(package_name)


def deb_get_pkg_version(package_name):
    code, output, stderr = run_dpkg_cmd_with_retry('dpkg -s ' + package_name + ' | grep Version:')
    if code == 0:
        code, output, stderr = run_dpkg_cmd_with_retry("dpkg -s " + package_name + " | grep Version: | awk '{print $2}'")
        if code == 0:
            return output


def rpm_remove_incomptible_dsc_package():
    code, version, stderr = run_cmd('rpm -q --queryformat "%{VERSION}.%{RELEASE}" dsc')
    if code == 0 and is_incomptible_dsc_package(version):
        rpm_uninstall_package('dsc')


def rpm_remove_old_oms_package(package_name, version):
    if rpm_check_old_oms_package(package_name, version):
        rpm_uninstall_package(package_name)


def rpm_check_old_oms_package(package_name, version):
    code, output, stderr = run_cmd('rpm -q ' + package_name)
    if code == 0 and is_old_oms_server(package_name):
        return True
    return False


def install_dsc_packages():
    openssl_version = get_openssl_version()
    omi_package_path = omi_package_prefix + openssl_version
    dsc_package_path = dsc_package_prefix + openssl_version
    compiler_mitigated_omi_flag = get_compiler_mitigated_omi_flag()
    waagent.AddExtensionEvent(name=ExtensionShortName, op='InstallInProgress', isSuccess=True,
                              message="Installing omipackage version: " + omi_package_path + "; dsc package version: " + dsc_package_path)
    if distro_category == DistroCategory.debian:
        deb_install_pkg(omi_package_path + '.ulinux' + compiler_mitigated_omi_flag + '.x64.deb', 'omi', omi_major_version, omi_minor_version, omi_build,
                        omi_release, ' --force-confold --force-confdef --refuse-downgrade ')
        deb_install_pkg(dsc_package_path + '.x64.deb', 'dsc', dsc_major_version, dsc_minor_version, dsc_build,
                        dsc_release, '')
    elif distro_category == DistroCategory.redhat or distro_category == DistroCategory.suse:
        rpm_install_pkg(omi_package_path + '.ulinux' + compiler_mitigated_omi_flag + '.x64.rpm', 'omi', omi_major_version, omi_minor_version, omi_build,
                        omi_release)
        rpm_install_pkg(dsc_package_path + '.x64.rpm', 'dsc', dsc_major_version, dsc_minor_version, dsc_build,
                        dsc_release)

def get_compiler_mitigated_omi_flag():
    vm_supported, vm_dist, vm_ver = check_supported_OS()

    if is_compiler_mitigated_omi_supported(vm_dist.lower(), vm_ver.lower()):
        return '.s'

    return ''

def is_compiler_mitigated_omi_supported(dist_name, dist_version):
    # Compiler-mitigated OMI is not supported in the following
    # SLES 11
    
    # To be enhanced if there are future distros not supporting compiler-mitigated OMI package
    if dist_name.startswith('sles') and dist_version.startswith('11'):
        return False
    
    return True

def compare_pkg_version(system_package_version, major_version, minor_version, build, release):
    version = re.match(package_pattern, system_package_version)
    if version is not None and ((int(version.group(1)) > major_version) or (
            int(version.group(1)) == major_version and int(version.group(2)) > minor_version) or (
                                        int(version.group(1)) == major_version and int(
                                    version.group(2)) == minor_version and int(version.group(3)) > build) or (
                                        int(version.group(1)) == major_version and int(
                                    version.group(2)) == minor_version and int(version.group(3)) == build and int(
                                    version.group(4)) >= release)):
        return 1
    return 0


def rpm_check_pkg_exists(package_name, major_version, minor_version, build, release):
    code, output, stderr = run_cmd('rpm -q --queryformat "%{VERSION}.%{RELEASE}" ' + package_name)
    waagent.AddExtensionEvent(name=ExtensionShortName, op='InstallInProgress', isSuccess=True,
                              message="package name: " + package_name + ";  existing package version:" + output)
    hutil.log("package name: " + package_name + ";  existing package version:" + output)
    if code == 0:
        return compare_pkg_version(output, major_version, minor_version, build, release)


def rpm_install_pkg(package_path, package_name, major_version, minor_version, build, release):
    if rpm_check_pkg_exists(package_name, major_version, minor_version, build, release) == 1:
        # package is already installed
        return
    else:
        code, output, stderr = run_cmd('rpm -Uvh ' + package_path)
        if code == 0:
            hutil.log(package_name + ' is installed successfully')
        else:
            waagent.AddExtensionEvent(name=ExtensionShortName, op='InstallInProgress', isSuccess=True,
                                      message="Failed to install RPM package :" + package_path)
            raise Exception('Failed to install package {0}: stdout: {1}, stderr: {2}'.format(package_name, output, stderr))


def deb_install_pkg(package_path, package_name, major_version, minor_version, build, release, install_options):
    version = deb_get_pkg_version(package_name)
    if version is not None and compare_pkg_version(version, major_version, minor_version, build, release) == 1:
        # package is already installed
        hutil.log(package_name + ' version ' + version + ' is already installed')
        waagent.AddExtensionEvent(name=ExtensionShortName, op='InstallInProgress', isSuccess=True,
                                  message="dsc package with version: " + version + "is already installed.")
        return
    else:
        cmd = 'dpkg -i ' + install_options + ' ' + package_path
        code, output, stderr = run_dpkg_cmd_with_retry(cmd)
        if code == 0:
            hutil.log(package_name + ' version ' + str(major_version) + '.' + str(minor_version) + '.' + str(
                build) + '.' + str(release) + ' is installed successfully')
        elif code == DPKGLockedErrorCode:
            hutil.do_exit(DPKGLockedErrorCode, 'Install', 'error', str(DPKGLockedErrorCode), 'Install failed because the package manager on the VM is currently locked. Please try installing again.')
        else:
            waagent.AddExtensionEvent(name=ExtensionShortName, op='InstallInProgress', isSuccess=False,
                                      message="Failed to install debian package :" + package_path)
            raise Exception('Failed to install package {0}: stdout: {1}, stderr: {2}'.format(package_name, output, stderr))


def install_package(package):
    if distro_category == DistroCategory.debian:
        apt_package_install(package)
    elif distro_category == DistroCategory.redhat:
        yum_package_install(package)
    elif distro_category == DistroCategory.suse:
        zypper_package_install(package)


def zypper_package_install(package):
    hutil.log('zypper --non-interactive in ' + package)
    code, output, stderr = run_cmd('zypper --non-interactive in ' + package)
    if code == 0:
        hutil.log('Package ' + package + ' is installed successfully')
    else:
        waagent.AddExtensionEvent(name=ExtensionShortName, op='InstallInProgress', isSuccess=True,
                                  message="Failed to install zypper package :" + package)
        raise Exception('Failed to install package {0}: stdout: {1}, stderr: {2}'.format(package, output, stderr))


def yum_package_install(package):
    hutil.log('yum install -y ' + package)
    code, output, stderr = run_cmd('yum install -y ' + package)
    if code == 0:
        hutil.log('Package ' + package + ' is installed successfully')
    else:
        waagent.AddExtensionEvent(name=ExtensionShortName, op='InstallInProgress', isSuccess=True,
                                  message="Failed to install yum package :" + package)
        raise Exception('Failed to install package {0}: stdout: {1}, stderr: {2}'.format(package, output, stderr))


def apt_package_install(package):
    hutil.log('apt-get install -y --force-yes ' + package)
    code, output, stderr = run_cmd('apt-get install -y --force-yes ' + package)
    if code == 0:
        hutil.log('Package ' + package + ' is installed successfully')
    else:
        waagent.AddExtensionEvent(name=ExtensionShortName, op='InstallInProgress', isSuccess=True,
                                  message="Failed to install apt package :" + package)
        raise Exception('Failed to install package {0}: stdout: {1}, stderr: {2}'.format(package, output, stderr))


def get_openssl_version():
    cmd_result = waagent.RunGetOutput("openssl version")
    cmd_result = cmd_result.decode() if isinstance(cmd_result, bytes) else cmd_result
    openssl_version = cmd_result[1].split()[1]
    if re.match('^1.0.*', openssl_version):
        return '100'
    elif re.match('^1.1.*', openssl_version):
        return '110'
    else:
        error_msg = 'This system does not have a supported version of OpenSSL installed. Supported version: 1.0.*, 1.1.*'
        hutil.error(error_msg)
        waagent.AddExtensionEvent(name=ExtensionShortName, op='InstallInProgress', isSuccess=True,
                                  message="System doesn't have supported OpenSSL version:" + openssl_version)
        hutil.do_exit(51, 'Install', 'error', '51', openssl_version + 'is not supported.')


def start_omiservice():
    run_cmd('/opt/omi/bin/service_control start')
    code, output, stderr =run_cmd('service omid status')
    if code == 0:
        hutil.log('Service omid is started')
    else:
        raise Exception('Failed to start service omid, status: stdout: {0}, stderr: {1}'.format(output, stderr))


def download_file():
    waagent.AddExtensionEvent(name=ExtensionShortName, op="EnableInProgress", isSuccess=True,
                              message="Downloading file")
    download_dir = prepare_download_dir(hutil.get_seq_no())
    storage_account_name = get_config('StorageAccountName')
    storage_account_key = get_config('StorageAccountKey')
    file_uri = get_config('FileUri')

    if not file_uri:
        error_msg = 'Missing FileUri configuration'
        waagent.AddExtensionEvent(name=ExtensionShortName,
                                  op=Operation.Download,
                                  isSuccess=False,
                                  message="(03000)Argument error, invalid file location")
        hutil.do_exit(51, 'Enable', 'error', '51', '(03000)Argument error, invalid file location')

    if storage_account_name and storage_account_key:
        hutil.log('Downloading file from azure storage...')
        path = download_azure_blob(storage_account_name, storage_account_key, file_uri, download_dir)
        return path
    else:
        hutil.log('Downloading file from external link...')
        waagent.AddExtensionEvent(name=ExtensionShortName, op="EnableInProgress", isSuccess=True,
                                  message="Downloading file from external link...")
        path = download_external_file(file_uri, download_dir)
        return path


def download_azure_blob(account_name, account_key, file_uri, download_dir):
    waagent.AddExtensionEvent(name=ExtensionShortName, op="EnableInProgress", isSuccess=True,
                              message="Downloading from azure blob")
    try:
        (blob_name, container_name) = parse_blob_uri(file_uri)
        host_base = get_host_base_from_uri(file_uri)

        blob_parent_path = os.path.join(download_dir, os.path.dirname(blob_name))
        if not os.path.exists(blob_parent_path):
            os.makedirs(blob_parent_path)

        download_path = os.path.join(download_dir, blob_name)
        blob_service = BlobService(account_name, account_key, host_base=host_base)
    except Exception as e:
        waagent.AddExtensionEvent(name=ExtensionShortName, op='DownloadInProgress', isSuccess=True,
                                  message='Enable failed with the azure storage error : {0}, stack trace: {1}'.format(
                                      str(e), traceback.format_exc()))
        hutil.error('Failed to enable the extension with error: %s, stack trace: %s' % (str(e), traceback.format_exc()))
        hutil.do_exit(1, 'Enable', 'error', '1', 'Enable failed: {0}'.format(e))

    max_retry = 3
    for retry in range(1, max_retry + 1):
        try:
            blob_service.get_blob_to_path(container_name, blob_name, download_path)
        except Exception:
            hutil.error('Failed to download Azure blob, retry = ' + str(retry) + ', max_retry = ' + str(max_retry))
            if retry != max_retry:
                hutil.log('Sleep 10 seconds')
                time.sleep(10)
            else:
                waagent.AddExtensionEvent(name=ExtensionShortName,
                                          op=Operation.Download,
                                          isSuccess=False,
                                          message="(03303)Failed to download file from Azure Storage")
                raise Exception('Failed to download azure blob: ' + blob_name)
    waagent.AddExtensionEvent(name=ExtensionShortName,
                              op=Operation.Download,
                              isSuccess=True,
                              message="(03301)Succeeded to download file from Azure Storage")
    return download_path


def parse_blob_uri(blob_uri):
    path = get_path_from_uri(blob_uri).strip('/')
    first_sep = path.find('/')
    if first_sep == -1:
        waagent.AddExtensionEvent(name=ExtensionShortName, op="EnableInProgress", isSuccess=False,
                                  message="Error occured while extracting container and blob name.")
        hutil.error("Failed to extract container and blob name from " + blob_uri)
    blob_name = path[first_sep + 1:]
    container_name = path[:first_sep]
    return (blob_name, container_name)


def get_path_from_uri(uri):
    uri = urlparse(uri)
    return uri.path


def get_host_base_from_uri(blob_uri):
    uri = urlparse(blob_uri)
    netloc = uri.netloc
    if netloc is None:
        return None
    return netloc[netloc.find('.'):]


def download_external_file(file_uri, download_dir):
    waagent.AddExtensionEvent(name=ExtensionShortName, op="EnableInProgress", isSuccess=True,
                              message="Downloading from external file")
    path = get_path_from_uri(file_uri)
    file_name = path.split('/')[-1]
    file_path = os.path.join(download_dir, file_name)
    max_retry = 3
    for retry in range(1, max_retry + 1):
        try:
            download_and_save_file(file_uri, file_path)
            waagent.AddExtensionEvent(name=ExtensionShortName, op=Operation.Download, isSuccess=True,
                                      message="(03302)Succeeded to download file from public URI")
            return file_path
        except Exception as e:
            hutil.error('Failed to download public file, retry = ' + str(retry) + ', max_retry = ' + str(max_retry))
            if retry != max_retry:
                hutil.log('Sleep 10 seconds')
                time.sleep(10)
            else:
                waagent.AddExtensionEvent(name=ExtensionShortName,
                                          op=Operation.Download,
                                          isSuccess=False,
                                          message='(03304)Failed to download file from public URI,  error : %s, stack trace: %s' % (
                                              str(e), traceback.format_exc()))
                raise Exception('Failed to download public file: ' + file_name)


def download_and_save_file(uri, file_path):
    src = urlopen(uri)
    dest = open(file_path, 'wb')
    buf_size = 1024
    buf = src.read(buf_size)
    while (buf):
        dest.write(buf)
        buf = src.read(buf_size)


def prepare_download_dir(seq_no):
    main_download_dir = os.path.join(os.getcwd(), DownloadDirectory)
    if not os.path.exists(main_download_dir):
        os.makedirs(main_download_dir)
    cur_download_dir = os.path.join(main_download_dir, seq_no)
    if not os.path.exists(cur_download_dir):
        os.makedirs(cur_download_dir)
    return cur_download_dir


def apply_dsc_configuration(config_file_path):
    cmd = dsc_script_path + '/StartDscConfiguration.py -configurationmof ' + config_file_path
    waagent.AddExtensionEvent(name=ExtensionShortName, op='EnableInProgress', isSuccess=True,
                              message='running the cmd: ' + cmd)
    code, output, stderr = run_cmd(cmd)
    if code == 0:
        code, output, stderr = run_cmd(dsc_script_path + '/GetDscConfiguration.py')
        return output
    else:
        error_msg = 'Failed to apply MOF configuration: stdout: {0}, stderr: {1}'.format(output, stderr)
        waagent.AddExtensionEvent(name=ExtensionShortName, op=Operation.ApplyMof, isSuccess=True, message=error_msg)
        hutil.error(error_msg)
        raise Exception(error_msg)


def apply_dsc_meta_configuration(config_file_path):
    cmd = dsc_script_path + '/SetDscLocalConfigurationManager.py -configurationmof ' + config_file_path
    waagent.AddExtensionEvent(name=ExtensionShortName, op='EnableInProgress', isSuccess=True,
                              message='running the cmd: ' + cmd)
    code, output, stderr = run_cmd(cmd)
    if code == 0:
        code, output, stderr = run_cmd(dsc_script_path + '/GetDscLocalConfigurationManager.py')
        return output
    else:
        error_msg = 'Failed to apply Meta MOF configuration: stdout: {0}, stderr: {1}'.format(output, stderr)
        hutil.error(error_msg)
        waagent.AddExtensionEvent(name=ExtensionShortName,
                                  op=Operation.ApplyMetaMof,
                                  isSuccess=False,
                                  message="(03107)" + error_msg)
        raise Exception(error_msg)


def get_statusfile_path():
    seq_no = hutil.get_seq_no()
    waagent.AddExtensionEvent(name=ExtensionShortName, op="EnableInProgress", isSuccess=True,
                              message="sequence number is :" + seq_no)
    status_file = None

    handlerEnvironment = None
    handler_env_path = os.path.join(os.getcwd(), 'HandlerEnvironment.json')
    try:
        with open(handler_env_path, 'r') as handler_env_file:
            handler_env_txt = handler_env_file.read()
        handler_env = json.loads(handler_env_txt)
        if type(handler_env) == list:
            handler_env = handler_env[0]
        handlerEnvironment = handler_env
    except Exception as e:
        hutil.error(e.message)
        waagent.AddExtensionEvent(name=ExtensionShortName, op="EnableInProgress", isSuccess=True,
                                  message='exception in retrieving status_dir error : %s, stack trace: %s' % (
                                      str(e), traceback.format_exc()))

    status_dir = handlerEnvironment['handlerEnvironment']['statusFolder']
    status_file = status_dir + '/' + seq_no + '.status'
    waagent.AddExtensionEvent(name=ExtensionShortName, op="EnableInProgress", isSuccess=True,
                              message="status file path: " + status_file)
    return status_file


def get_status_message_details():
    agent_id = get_nodeid(nodeid_path)
    vm_uuid = get_vmuuid()
    status_file_path = None
    if vm_uuid is not None and agent_id is not None:
        status_file_path = get_statusfile_path()

    return status_file_path, agent_id, vm_uuid


def update_statusfile(status_filepath, node_id, vmuuid, response):
    waagent.AddExtensionEvent(name=ExtensionShortName, op="EnableInProgress", isSuccess=True,
                              message="updating the status file " + '[statusfile={0}][vmuuid={1}][node_id={2}]'.format(
                                  status_filepath, vmuuid, node_id))
    if status_filepath is None:
        error_msg = "Unable to locate a status file"
        hutil.error(error_msg)
        waagent.AddExtensionEvent(name=ExtensionShortName, op="EnableInProgress", isSuccess=False, message=error_msg)
        return None

    status_data = None
    if os.path.exists(status_filepath):
        jsonData = open(status_filepath)
        status_data = json.load(jsonData)
        jsonData.close()

    accountName = response.deserialized_data["AccountName"]
    rgName = response.deserialized_data["ResourceGroupName"]
    subId = response.deserialized_data["SubscriptionId"]

    metadatastatus = [{"status": "success", "code": "0", "name": "metadata", "formattedMessage": {"lang": "en-US",
                                                                                                  "message": "AgentID=" + node_id + ";VMUUID=" + vmuuid + ";AutomationAccountName=" + accountName + ";ResourceGroupName=" + rgName + ";Subscription=" + subId}}]
    with open(status_filepath, "w") as fp:
        status_file_content = [{"status":
                                    {"status": "success",
                                     "formattedMessage": {"lang": "en-US", "message": "Enable Succeeded"},
                                     "operation": "Enable", "code": "0", "name": "Microsoft.OSTCExtensions.DSCForLinux",
                                     "substatus": metadatastatus
                                     },
                                "version": "1.0", "timestampUTC": time.strftime(date_time_format, time.gmtime())
                                }]
        json.dump(status_file_content, fp)
    waagent.AddExtensionEvent(name=ExtensionShortName, op="EnableInProgress", isSuccess=True,
                              message="successfully written nodeid and vmuuid")
    waagent.AddExtensionEvent(name=ExtensionName, op="Enable", isSuccess=True,
                              message="successfully executed enable functionality")


def get_nodeid(file_path):
    id = None
    try:
        if os.path.exists(file_path):
            with open(file_path) as f:
                id = f.readline().strip()
    except Exception as e:
        error_msg = 'get_nodeid() failed: Unable to open id file {0}'.format(file_path)
        hutil.error(error_msg)
        waagent.AddExtensionEvent(name=ExtensionShortName, op="EnableInProgress", isSuccess=False, message=error_msg)
        return None
    if not id:
        error_msg = 'get_nodeid() failed: Empty content in id file {0}'.format(file_path)
        hutil.error(error_msg)
        waagent.AddExtensionEvent(name=ExtensionShortName, op="EnableInProgress", isSuccess=False, message=error_msg)
        return None
    return id


def get_vmuuid():
    UUID = None
    code, output, stderr = run_cmd("sudo dmidecode | grep UUID | sed -e 's/UUID: //'")
    if code == 0:
        UUID = output.strip()
    return UUID


def get_omscloudid():
    OMSCLOUD_ID = None
    code, output, stderr = run_cmd("sudo dmidecode | grep 'Tag: 77' | sed -e 's/Asset Tag: //'")
    if code == 0:
        OMSCLOUD_ID = output.strip()
    return OMSCLOUD_ID


def check_dsc_configuration(current_config):
    outputlist = re.split("\n", current_config)
    for line in outputlist:
        if re.match(r'ReturnValue=0', line.strip()):
            return True
    return False


def install_module(file_path):
    install_package('unzip')
    cmd = dsc_script_path + '/InstallModule.py ' + file_path
    code, output, stderr = run_cmd(cmd)
    waagent.AddExtensionEvent(name=ExtensionShortName,
                              op="InstallModuleInProgress",
                              isSuccess=True,
                              message="Running the cmd: " + cmd)
    if not code == 0:
        error_msg = 'Failed to install DSC Module ' + file_path + ' stdout: {0}, stderr: {1}'.format(output, stderr)
        hutil.error(error_msg)
        waagent.AddExtensionEvent(name=ExtensionShortName,
                                  op=Operation.InstallModule,
                                  isSuccess=False,
                                  message="(03100)" + error_msg)
        raise Exception(error_msg)
    waagent.AddExtensionEvent(name=ExtensionShortName,
                              op=Operation.InstallModule,
                              isSuccess=True,
                              message="(03101)Succeeded to install DSC Module")


def remove_module():
    module_name = get_config('ResourceName')
    cmd = dsc_script_path + '/RemoveModule.py ' + module_name
    code, output, stderr = run_cmd(cmd)
    waagent.AddExtensionEvent(name=ExtensionShortName,
                              op="RemoveModuleInProgress",
                              isSuccess=True,
                              message="Running the cmd: " + cmd)
    if not code == 0:
        error_msg = 'Failed to remove DSC Module ' + module_name + ' stdout: {0}, stderr: {1}'.format(output, stderr)
        hutil.error(error_msg)
        waagent.AddExtensionEvent(name=ExtensionShortName,
                                  op=Operation.RemoveModule,
                                  isSuccess=False,
                                  message="(03102)" + error_msg)
        raise Exception(error_msg)
    waagent.AddExtensionEvent(name=ExtensionShortName,
                              op=Operation.RemoveModule,
                              isSuccess=True,
                              message="(03103)Succeeded to remove DSC Module")


def uninstall_package(package_name):
    waagent.AddExtensionEvent(name=ExtensionShortName, op='InstallInProgress', isSuccess=True,
                              message="uninstalling the package" + package_name)
    if distro_category == DistroCategory.debian:
        deb_uninstall_package(package_name)
    elif distro_category == DistroCategory.redhat or distro_category == DistroCategory.suse:
        rpm_uninstall_package(package_name)


def deb_uninstall_package(package_name):
    cmd = 'dpkg -P ' + package_name
    code, output, stderr = run_dpkg_cmd_with_retry(cmd)
    if code == 0:
        hutil.log('Package ' + package_name + ' was removed successfully')
    elif code == DPKGLockedErrorCode:
        hutil.do_exit(DPKGLockedErrorCode, 'Install', 'error', str(DPKGLockedErrorCode), 'Operation failed because the package manager on the VM is currently locked. Please try again.')
    else:
        waagent.AddExtensionEvent(name=ExtensionShortName, op='InstallInProgress', isSuccess=True,
                                  message="failed to remove the package" + package_name)
        raise Exception('Failed to remove package ' + package_name)


def rpm_uninstall_package(package_name):
    cmd = 'rpm -e ' + package_name
    code, output, stderr = run_cmd(cmd)
    if code == 0:
        hutil.log('Package ' + package_name + ' was removed successfully')
    else:
        waagent.AddExtensionEvent(name=ExtensionShortName, op='InstallInProgress', isSuccess=True,
                                  message="failed to remove the package" + package_name)
        raise Exception('Failed to remove package ' + package_name)
        
def is_dpkg_locked(exit_code, output):
    """
    If dpkg is locked, the output will contain a message similar to 'dpkg
    status database is locked by another process'
    """
    if exit_code is not 0:
        dpkg_locked_search = r'^.*dpkg.+lock.*$'
        dpkg_locked_re = re.compile(dpkg_locked_search, re.M)
        if dpkg_locked_re.search(output):
            return True
    return False


def register_automation(registration_key, registation_url, node_configuration_name, refresh_freq,
                        configuration_mode_freq, configuration_mode):
    if (registration_key == '' or registation_url == ''):
        err_msg = "Either the Registration Key or Registration URL is NOT provided"
        hutil.error(err_msg)
        waagent.AddExtensionEvent(name=ExtensionShortName, op='RegisterInProgress', isSuccess=True, message=err_msg)
        return 51, err_msg
    if configuration_mode != '' and not (
            configuration_mode == 'applyandmonitor' or configuration_mode == 'applyandautocorrect' or configuration_mode == 'applyonly'):
        err_msg = "ConfigurationMode: " + configuration_mode + " is not valid."
        hutil.error(err_msg + "It should be one of the values : (ApplyAndMonitor | ApplyAndAutoCorrect | ApplyOnly)")
        waagent.AddExtensionEvent(name=ExtensionShortName, op='RegisterInProgress', isSuccess=True, message=err_msg)
        return 51, err_msg
    cmd = dsc_script_path + '/Register.py' + ' --RegistrationKey ' + registration_key \
          + ' --ServerURL ' + registation_url
    optional_parameters = ""
    if node_configuration_name != '':
        optional_parameters += ' --ConfigurationName ' + node_configuration_name
    if refresh_freq != '':
        optional_parameters += ' --RefreshFrequencyMins ' + refresh_freq
    if configuration_mode_freq != '':
        optional_parameters += ' --ConfigurationModeFrequencyMins ' + configuration_mode_freq
    if configuration_mode != '':
        optional_parameters += ' --ConfigurationMode ' + configuration_mode
    waagent.AddExtensionEvent(name=ExtensionShortName,
                              op="RegisterInProgress",
                              isSuccess=True,
                              message="Registration URL " + registation_url + "Optional parameters to Registration" + optional_parameters)
    code, output, stderr = run_cmd(cmd + optional_parameters)
    if not code == 0:
        error_msg = '(03109)Failed to register with Azure Automation DSC: stdout: {0}, stderr: {1}'.format(output, stderr)
        hutil.error(error_msg)
        waagent.AddExtensionEvent(name=ExtensionShortName,
                                  op=Operation.Register,
                                  isSuccess=False,
                                  message=error_msg)
        return 1, error_msg
    waagent.AddExtensionEvent(name=ExtensionShortName,
                              op=Operation.Register,
                              isSuccess=True,
                              message="(03108)Succeeded to register with Azure Automation DSC")
    return 0, ''


if __name__ == '__main__':
    main()
