Providers/Scripts/2.6x-2.7x/Scripts/nxOMSContainers.py (222 lines of code) (raw):
#!/usr/bin/env python
#============================================================================
# Copyright (C) Microsoft Corporation, All rights reserved.
#============================================================================
import os
import sys
import imp
import re
import pwd
import grp
import codecs
import shutil
protocol = imp.load_source('protocol', '../protocol.py')
nxDSCLog = imp.load_source('nxDSCLog', '../nxDSCLog.py')
LG = nxDSCLog.DSCLog
MODULE_RESOURCE_DIR ='/opt/microsoft/omsconfig/modules/nxOMSContainers/DSCResources/MSFT_nxOMSContainersResource/containers'
PLUGIN_PATH = '/opt/microsoft/omsagent/plugin/'
CONF_PATH = '/etc/opt/microsoft/omsagent/<WorkspaceID>/conf/omsagent.d'
REG_PATH = '/etc/opt/omi/conf/omiregister/root-cimv2'
LIB_PATH = '/opt/microsoft/docker-cimprov/lib'
YAML_PATH = '/var/opt/microsoft/docker-cimprov/state'
STATE_DIR_PATH = '/var/opt/microsoft/omsagent/<WorkspaceID>/state'
PLUGIN_DIR = os.path.join(MODULE_RESOURCE_DIR, "plugin")
CONF_DIR = os.path.join(MODULE_RESOURCE_DIR, "conf")
REG_DIR = os.path.join(MODULE_RESOURCE_DIR, "reg")
LIB_DIR = os.path.join(MODULE_RESOURCE_DIR, "lib")
YAML_DIR = os.path.join(MODULE_RESOURCE_DIR, "yaml")
class IOMSAgent:
    def restart_oms_agent(self):
        pass
    
class OMSAgentUtil(IOMSAgent):
    def restart_oms_agent(self, WorkspaceID):
        wsId = WorkspaceID
        if wsId is None:
            wsId = ''
        if os.system('sudo /opt/microsoft/omsagent/bin/service_control restart ' + wsId) == 0:
            return True
        else:
            LG().Log('ERROR', 'Error restarting omsagent for workspace ' + wsId)
            return False
class TestOMSAgent(IOMSAgent):
    def restart_oms_agent(self):
        return True
OMS_ACTION = OMSAgentUtil()
def init_vars(WorkspaceID = '', Ensure = ''):
    global CONF_PATH
    WorkspaceID = WorkspaceID.encode('ascii', 'ignore')
    Ensure = Ensure.encode('ascii', 'ignore')
    CONF_PATH = CONF_PATH.replace('<WorkspaceID>',WorkspaceID)
def Set_Marshall(WorkspaceID, Ensure):
    init_vars(WorkspaceID, Ensure)
    return Set(WorkspaceID, Ensure)
def Test_Marshall(WorkspaceID, Ensure):
    init_vars(WorkspaceID, Ensure)
    return Test(WorkspaceID, Ensure)
def Get_Marshall(WorkspaceID, Ensure):
    init_vars(WorkspaceID, Ensure)
    retval = 0
    retd = dict()
    retd['WorkspaceID'] = WorkspaceID
    retd['Ensure'] = Ensure
    return retval, retd
 
def Set(WorkspaceID, Ensure):
    if Ensure == 'Present':
        # copy all the required files to the client machine
        copy_all_files(CONF_DIR, CONF_PATH)
        update_state_dir_path(os.path.join(CONF_PATH, "container.conf"), WorkspaceID)
        copy_all_files(PLUGIN_DIR, PLUGIN_PATH)
        copy_all_files(LIB_DIR, LIB_PATH)
        copy_all_files(REG_DIR, REG_PATH)
        check_and_create_yaml(YAML_DIR, YAML_PATH)
    elif Ensure == 'Absent':
        # and delete CONF file in the directory
        delete_all_files(CONF_DIR, CONF_PATH)
    else:
        # log error Ensure value not expected
        LG().Log('ERROR', "Ensure value: " + Ensure + " not expected")
        return [-1]
    
    # restart oms agent
    if OMS_ACTION.restart_oms_agent(WorkspaceID):
        return [0]
    else:
        return [-1]
def Test(WorkspaceID, Ensure):
    """
    Test method for the DSC resoruce
    If it returns [0] no further action is taken
    If it returns [-1] Set_Marshall is called
    """
    # test for the existence of plugin and conf subfolders in the current plugin
    if Ensure == 'Present':
        # check all files exist
        if (not check_all_files(PLUGIN_DIR, PLUGIN_PATH) or not check_all_files(CONF_DIR, CONF_PATH) or not check_all_files(LIB_DIR, LIB_PATH) or not check_all_files(REG_DIR, REG_PATH) or not check_all_files(YAML_DIR, YAML_PATH)):
            return [-1]
    elif Ensure == 'Absent':
        if (check_conf_presence(conf_dir, CONF_PATH)):
            return [-1]
    else:
        # log error Ensure value not expected
        LG().Log('ERROR', "Ensure value: " + Ensure + " not expected")
        return [-1]
def update_state_dir_path(dest, WorkspaceID):
    replace_text = STATE_DIR_PATH.replace('<WorkspaceID>',WorkspaceID)
    with open(dest) as f:
        s = f.read()
    with open(dest, 'w') as f:
        s = s.replace('%STATE_DIR_WS%', replace_text)
        f.write(s)
def copy_all_files(src, dest):
    try:
        src_files = os.listdir(src)
        for file_name in src_files:
            full_file_name = os.path.join(src, file_name)
            if (os.path.isfile(full_file_name)):
                shutil.copy(full_file_name, dest)
    except:
        LG().Log('ERROR', 'copy_all_files failed for src: ' + src + ' dest: ' + dest)
        return False
def check_conf_presence(src, dest):
    src_files = os.listdir(src)
    for file_name in src_files:
        full_dest_file = os.path.join(dest, file_name)
        if os.path.isfile(full_dest_file):
            return True
    return False
def check_and_create_yaml(src, dest):
    try:
        src_files = os.listdir(src)
        for file_name in src_files:
            full_file_name = os.path.join(src, file_name)
            if (not os.path.isfile(full_file_name)):
                shutil.copy(full_file_name, dest)
                os.chmod(os.path.join(dest, file_name), 0644)
                os.chown(os.path.join(dest, file_name), GetUID('omsagent'), GetGID('omiusers'))
    except:
        LG().Log('ERROR', 'copy_all_files failed for src: ' + src + ' dest: ' + dest)
        return False
            
def delete_all_files(src, dest):
    try:
        src_files = os.listdir(src)
        for file_name in src_files:
            full_file_name = os.path.join(dest, file_name)
            if (os.path.isfile(full_file_name)):
                os.remove(full_file_name)
    except:
        LG().Log('ERROR', 'delete_all_files failed for src: ' + src + ' dest: ' + dest)
        return False
def check_all_files(src, dest):
    try:
        src_files = os.listdir(src)
        for file_name in src_files:
            full_src_file = os.path.join(src, file_name)
            full_dest_file = os.path.join(dest, file_name)
            if os.path.isfile(full_dest_file):
                if CompareFiles(full_dest_file, full_src_file, "md5") == -1:
                    return False
            else:
                return False
        return True
    except:
        LG().Log('ERROR', 'check_all_files failed for src: ' + src + ' dest: ' + dest)
        return False
def CompareFiles(DestinationPath, SourcePath, Checksum):
    """
    If the files differ in size, return -1.
    Reading and computing the hash here is done in a block-by-block manner,
    in case the file is quite large.
    """
    if SourcePath == DestinationPath:  # Files are the same!
        return 0
    stat_dest = StatFile(DestinationPath)
    stat_src = StatFile(SourcePath)
    if stat_src.st_size != stat_dest.st_size:
        return -1
    if Checksum == "md5":
        src_error = None
        dest_error = None
        src_hash = md5const()
        dest_hash = md5const()
        src_block = 'loopme'
        dest_block = 'loopme'
        src_file,src_error = opened_bin_w_error(SourcePath, 'rb')
        if src_error:
            LG().Log('ERROR', "Exception opening source file " + SourcePath + " Error : " + str(src_error))
            return -1
        dest_file, dest_error = opened_bin_w_error(DestinationPath, 'rb')
        if dest_error:
            LG().Log('ERROR', "Exception opening destination file " + DestinationPath + " Error : " + str(dest_error))
            src_file.close()
            return -1
        while src_block != '' and dest_block != '':
            src_block = src_file.read(BLOCK_SIZE)
            dest_block = dest_file.read(BLOCK_SIZE)
            src_hash.update(src_block)
            dest_hash.update(dest_block)
            if src_hash.hexdigest() != dest_hash.hexdigest():
                src_file.close()
                dest_file.close()
                return -1  
        if src_hash.hexdigest() == dest_hash.hexdigest():
            src_file.close()
            dest_file.close()
            return 0  
    elif Checksum == "ctime":
        if stat_src.st_ctime != stat_dest.st_ctime:
            return -1
        else:
            return 0
    elif Checksum == "mtime":
        if stat_src.st_mtime != stat_dest.st_mtime:
            return -1
        else:
            return 0
def GetUID(User):
    uid = None
    try:
        uid = pwd.getpwnam(User)[2]
    except KeyError:
        LG().Log('ERROR', 'ERROR: Unknown UID for ' + User)
    return uid
def GetGID(Group):
    gid = None
    try:
        gid = grp.getgrnam(Group)[2]
    except KeyError:
        LG().Log('ERROR', 'ERROR: Unknown GID for ' + Group)
    return gid
def StatFile(path):
    """
    Stat the file, following the symlink.
    """
    d = None
    error = None
    try:
        d = os.stat(path)
    except OSError as error:
        LG().Log('ERROR', "Exception stating file " + path  + " Error: " + str(error))
    except IOError as error:
        LG().Log('ERROR', "Exception stating file " + path  + " Error: " + str(error))
    return d
    
def opened_bin_w_error(filename, mode='rb'):
    """
    This context ensures the file is closed.
    """
    try:
        f = open(filename, mode)
    except IOError, error:
        return None, error
    return f, None