Beispiel #1
0
class SHchkconfig(ServiceHelperTemplate):
    """SHchkconfig is the Service Helper for systems using the chkconfig command to
    configure services. (RHEL up to 6, SUSE, Centos up to 6, etc)
    
    @author: David Kennel


    """
    def __init__(self, environment, logdispatcher):
        """
        Constructor
        """
        super(SHchkconfig, self).__init__(environment, logdispatcher)
        self.environ = environment
        self.logger = logdispatcher
        self.initobjs()
        self.localize()

    def initobjs(self):
        """initialize class objects"""

        self.ch = CommandHelper(self.logger)

    def localize(self):
        """set base command paths (chkconfig and service) based on OS"""

        self.svc = ""
        self.chk = ""

        chk_paths = ["/sbin/chkconfig", "/usr/sbin/chkconfig"]
        for cp in chk_paths:
            if os.path.exists(cp):
                self.chk = cp
                break
        service_paths = ["/sbin/service", "/usr/sbin/service"]
        for sp in service_paths:
            if os.path.exists(sp):
                self.svc = sp
                break

        if not self.svc:
            raise IOError(
                "Could not locate the service utility on this system")
        if not self.chk:
            raise IOError(
                "Could not locate the chkconfig utility on this system")

    def startService(self, service, **kwargs):
        """start a given service

        :param service: string; name of service
        :param kwargs: return: success
        :param **kwargs: 
        :returns: success
        :rtype: bool
@author: Breen Malmberg

        """

        success = True

        self.ch.executeCommand(self.svc + " " + service + " start")
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            success = False

        if not self.isRunning(service):
            success = False

        return success

    def stopService(self, service, **kwargs):
        """stop a given service

        :param service: param kwargs:
        :param **kwargs: 
        :returns: success
        :rtype: bool
@author: Breen Malmberg

        """

        success = True

        self.ch.executeCommand(self.svc + " " + service + " stop")
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            success = False

        if self.isRunning(service):
            success = False

        return success

    def disableService(self, service, **kwargs):
        """Disables the specified service and stops it if
        it is running

        :param service: string; Name of the service to be disabled
        :param **kwargs: 
        :returns: bool
        @author: David Kennel
        @change: Breen Malmberg - 04/10/2019 - method refactor; doc string edit;
                logging edit

        """

        disabled = True

        self.ch.executeCommand(self.chk + " " + service + " off")
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            disabled = False

        if self.auditService(service):
            disabled = False

        if not self.stopService(service):
            disabled = False

        return disabled

    def enableService(self, service, **kwargs):
        """Enables a service and starts it if it is not running as long as we are
        not in install mode

        :param service: string; Name of the service to be enabled
        :param **kwargs: 
        :returns: enabled
        :rtype: bool
@author: David Kennel
@change: Breen Malmberg - 04/10/2019 -

        """

        enabled = True

        self.ch.executeCommand(self.chk + " " + service + " on")
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            enabled = False

        if not self.auditService(service):
            enabled = False

        if not self.startService(service):
            enabled = False

        return enabled

    def auditService(self, service, **kwargs):
        """Checks the status of a service and returns a bool indicating whether or
        not the service is enabled

        :param service: string; Name of the service to audit
        :param **kwargs: 
        :returns: enabled
        :rtype: bool
@author: ???
@change: Breen Malmberg - 04/10/2019 - method refactor; doc string edit;
        logging edit

        """

        enabled = True

        if not self.audit_chkconfig_service(service):
            enabled = False

        return enabled

    def audit_chkconfig_service(self, service):
        """uses the chkconfig command to check if a given
        service is enabled or not

        :param service: 
        :returns: enabled
        :rtype: bool
@author: Breen Malmberg

        """

        enabled = True

        self.ch.executeCommand(self.chk + " --list " + service)
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            enabled = False
            self.logger.log(LogPriority.DEBUG,
                            "Failed to get status of service: " + service)
            return enabled

        output = self.ch.getOutputString()
        if not re.search(":on", output):
            enabled = False

        return enabled

    def isRunning(self, service, **kwargs):
        """Check to see if a service is currently running.

        :param service: string; Name of the service to check
        :param **kwargs: 
        :returns: running
        :rtype: bool
@author: ???
@change: Breen Malmberg - 04/10/2019 - method refactor; doc string edit;
        logging edit

        """

        running = True
        # see: http://refspecs.linuxbase.org/LSB_3.1.0/LSB-generic/LSB-generic/iniscrptact.html
        success_codes = [0]

        self.ch.executeCommand(self.svc + " " + service + " status")
        retcode = self.ch.getReturnCode()
        if retcode not in success_codes:
            running = False
            self.logger.log(
                LogPriority.DEBUG,
                "Command error while getting run status of service: " +
                service)
            return running

        outputlines = self.ch.getOutput()
        # need to parse for either sysv or systemd output
        if not self.parse_running(outputlines):
            running = False

        return running

    def parse_running(self, outputlines):
        """check whether given service is running, with the
        service command
        this is the older (classic) systemV case

        :param outputlines: list; list of strings to search
        :returns: running
        :rtype: bool
@author: Breen Malmberg

        """

        running = True
        systemctl_locations = ["/usr/bin/systemctl", "/bin/systemctl"]
        if any(os.path.exists(sl) for sl in systemctl_locations):
            searchterms = ["Active:\s+inactive", "Active:\s+unknown"]
        else:
            searchterms = [
                "is stopped", "hook is not installed", "is not running"
            ]

        for line in outputlines:
            if any(re.search(st, line) for st in searchterms):
                running = False
                break

        return running

    def reloadService(self, service, **kwargs):
        """Reload (HUP) a service so that it re-reads it's config files. Called
        by rules that are configuring a service to make the new configuration
        active.

        :param service: string; Name of service to be reloaded
        :param **kwargs: 
        :returns: reloaded
        :rtype: bool
@author: ???
@change: Breen Malmberg - 04/10/2019 - method refactor; doc string edit;
        logging edit

        """

        reloaded = True

        # force-reload: cause the configuration to be reloaded if the service supports this,
        # otherwise restart the service if it is running
        self.ch.executeCommand(self.svc + " " + service + " force-reload")
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            reloaded = False
            self.logger.log(LogPriority.DEBUG,
                            "Failed to reload service: " + service)

        return reloaded

    def listServices(self, **kwargs):
        """Return a list containing strings that are service names.

        :param **kwargs: 
        :returns: service_list
        :rtype: list
@author: ???
@change: Breen Malmberg - 04/10/2019 - method refactor; doc string edit;
        logging edit

        """

        service_list = []

        self.ch.executeCommand(self.chk + " --list")
        outputlines = self.ch.getOutput()
        for line in outputlines:
            try:
                service_list.append(line.split()[0])
            except IndexError:
                pass

        return service_list

    def getStartCommand(self, service):
        """retrieve the start command.  Mostly used by event recording

        :param service: 
        :returns: string - start command
        @author: dwalker

        """
        return self.svc + " " + service + " start"

    def getStopCommand(self, service):
        """retrieve the stop command.  Mostly used by event recording

        :param service: 
        :returns: string - stop command
        @author: dwalker

        """
        return self.svc + " " + service + " stop"

    def getEnableCommand(self, service):
        """retrieve the enable command.  Mostly used by event recording

        :param service: 
        :returns: string - enable command
        @author: dwalker

        """
        return self.chk + " " + service + " on"

    def getDisableCommand(self, service):
        """retrieve the start command.  Mostly used by event recording

        :param service: 
        :returns: string - disable command
        @author: dwalker

        """
        return self.chk + " " + service + " off"
Beispiel #2
0
class MacPkgr(object):

    def __init__(self, environ, logger):
        '''
        Mac package manager based on other stonix package managers.

        Uses the stonix IHmac and InstallingHelper Libraries.  They can
        install .zip, .tar, .tar.gz, .pkg and .mpkg files via an http or
        https URL

        :param environ: environment object
        :param logger: logdispatcher object

        # Methods specific to Mac.

        @note: Uses the stonix IHmac and InstallingHelper Libraries.  They can
               install .zip, .tar, .tar.gz, .pkg and .mpkg files via an http or
               https URL

        @note: WARNING: To use checkInstall or removepackage, this package
               manager converts all of the plists in the /var/db/receipts
               directory to text, then converts they all back to binary when it
               is done performing a reverse lookup to find if a specific
               package is installed. I would love to use Greg Neagle's
               FoundationPlist.py, but licenses are not compatible.
        @change: Breen Malmberg - 2/28/2017 - Made the missing macreporoot message more
                clear and helpful; added logic to also check if it is 'None'
        '''

        self.environ = environ

        self.osfamily = self.environ.getosfamily()
        if not re.match("^darwin$", self.osfamily.strip()):
            return

        #####
        # setting up to call ctypes to do a filesystem sync
        if self.environ.getosfamily() == "darwin":
            self.libc = C.CDLL("/usr/lib/libc.dylib")
        else:
            self.libc = None

        self.logger = logger
        self.detailedresults = ""
        self.pkgUrl = ""
        self.reporoot = ""
        if not MACREPOROOT:
            raise NoRepoException("Please ensure that the constant, MACREPOROOT, is properly defined in localize.py and is not set to 'None'")
        elif MACREPOROOT == None:
            raise NoRepoException("Please ensure that the constant, MACREPOROOT, is properly defined in localize.py and is not set to 'None'")
        else:
            self.reporoot = MACREPOROOT
        self.dotmd5 = True
        self.logger.log(LogPriority.DEBUG,
                        "Done initializing MacPkgr class...")
        self.ch = CommandHelper(self.logger)
        self.connection = Connectivity(self.logger)

    def installPackage(self, package):
        '''
        Install a package. Return a bool indicating success or failure.

        :param package: Path to the package past the REPOROOT
        
        IE. package would be: QuickAdd.Stonix.pkg rather than:
            https://jss.lanl.gov/CasperShare/QuickAdd.Stonix.pkg
            \_____________________________/ \________________/
                            |                        |
                        REPOROOT                  package
        
        Where REPOROOT is initialized in the class __init__, and package is
        passed in to this method
        
        This assumes that all packages installed via an instance of this class
        will be retrieved from the same REPOROOT.  If you need to use another
        REPOROOT, please use another instance of this class.

        :returns: success
        :rtype: bool
@author: dwalker, Roy Nielsen
@change: Breen Malmberg - 2/28/2017 - added logic to handle case where
        self.reporoot is undefined; added logging; minor doc string edit

        '''

        success = False

        if not self.reporoot:
            self.logger.log(LogPriority.WARNING, "No MacRepoRoot defined! Unable to determine package URL!")
            return success

        try:

            self.package = package
            #####
            # Create a class variable that houses the whole URL
            if self.reporoot.endswith("/"):
                self.pkgUrl = self.reporoot + self.package
            else:
                self.pkgUrl = self.reporoot + "/" + self.package
            message = "self.pkgUrl: " + str(self.pkgUrl)
            self.logger.log(LogPriority.DEBUG, message)

            if re.search("://", self.pkgUrl):
                if self.connection.isPageAvailable(self.pkgUrl):
                    #####
                    # Download into a temporary directory
                    success = self.downloadPackage()
                    if success:
                        #####
                        # Apple operating systems have a lazy attitude towards
                        # writing to disk - the package doesn't get fully
                        # written to disk until the following method is called.
                        # Otherwise when the downloaded package is further
                        # manipulated, (uncompressed or installed) the
                        # downloaded file is not there.  There may be other
                        # ways to get python to do the filesystem sync...
                        try:
                            self.libc.sync()
                        except:
                            pass
                        #####
                        # Make sure the md5 of the file matches that of the
                        # server
                        if self.checkMd5():
                            #####
                            # unarchive if necessary
                            compressed = [".tar", ".tar.gz", ".tgz",
                                          ".tar.bz", ".tbz", ".zip"]
                            for extension in compressed:
                                if self.tmpLocalPkg.endswith(extension):
                                    self.unArchive()
                                try:
                                    self.libc.sync()
                                except:
                                    pass
                            #####
                            # The unArchive renames self.tmpLocalPkg to the
                            # actual software to install that is inside the
                            # package.
                            #
                            # install - if extension is .app, copy to the
                            #           /Applications folder, otherwise if it
                            #           is a .pkg or .mpkg use the installer
                            #           command
                            if self.tmpLocalPkg.endswith(".app"):
                                success = self.copyInstall()

                            elif self.tmpLocalPkg.endswith(".pkg") or \
                                 self.tmpLocalPkg.endswith(".mpkg"):
                                success = self.installPkg()
            else:
                #####
                # Otherwise the repository is on a mounted filesystem
                self.logger.log(LogPriority.DEBUG, "Looking for a local " +
                                                   "filesystem repo...")
                self.tmpLocalPkg = self.reporoot + self.package

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception as err:
            print(err)
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.DEBUG, self.detailedresults)
            raise err
        if success:
            try:
                self.libc.sync()
            except:
                pass
        return success

    ###########################################################################

    def getPkgUrl(self):
        '''Setter for the class varialbe pkgUrl
        
        @author: Roy Nielsen


        '''
        return self.pkgUrl

    ###########################################################################

    def setPkgUrl(self, pkgUrl=""):
        '''Setter for the class varialbe pkgUrl
        
        @author: Roy Nielsen

        :param pkgUrl:  (Default value = "")

        '''
        self.pkgUrl = pkgUrl

    ###########################################################################

    def removePackage(self, package="", install_root="/"):
        '''Remove a package domain. Return a bool indicating success or failure.
        Not yet implemented...
        
        Will use pkgutil to determine domain, then delete files in receipt..

        :param string: package : Name of the package to be removed, must be
            recognizable to the underlying package manager.
        :param package:  (Default value = "")
        :param install_root:  (Default value = "/")
        :returns: bool :
        @author: rsn

        '''
        success = False
        self.package = package
        try:
            self.logger.log(LogPriority.DEBUG, "Package: " + str(package))
            domain = None
            domain = self.findDomain(package)
            self.logger.log(LogPriority.DEBUG,
                            "removePackage - Domain: " + domain)
            if domain:
                cmd_one = ["/usr/sbin/pkgutil",
                           "--only-files",
                           "--files",
                           domain]
                cmd_two = ["/usr/sbin/pkgutil",
                           "--only-dirs",
                           "--files",
                           domain]

                #####
                # Use the pkgutil command to get a list of files in the package
                # receipt
                count = 0
                self.ch.executeCommand(cmd_one)
                files2remove = self.ch.getOutputString().split("\n")
                print(("Files to remove: " + str(files2remove)))
                self.logger.log(LogPriority.DEBUG, files2remove)
                if str(self.ch.getReturnCode()) == str(0):
                    for file in files2remove:
                        if file:
                            try:
                                #####
                                # Make sure "/" is prepended to the file as
                                # pkgutil does not report the first "/" in the
                                # file path
                                os.remove(install_root + file)
                                count = count + 1
                            except OSError as err:
                                self.logger.log(LogPriority.DEBUG,
                                                "Error trying to remove: " +
                                                str(file))
                                self.logger.log(LogPriority.DEBUG,
                                                "With Exception: " +
                                                str(err))
                        else:
                            #####
                            # Potentially empty filename in the list, need to
                            # bump the count to match.
                            count = count + 1

                    #####
                    # Directory list will include directories such as /usr
                    # and /usr/local... Sucess is obtained only if all of
                    # the files (not directories) are deleted.
                    if count == len(files2remove):
                        success = True
                    else:
                        self.logger.log(LogPriority.WARNING,
                                        "Count: " + str(count))
                        self.logger.log(LogPriority.WARNING,
                                        "Files removed: " +
                                        str(len(files2remove)))

                    #####
                    # Use the pkgutil command to get a list of directories
                    # in the package receipt
                    self.ch.executeCommand(cmd_two)
                    dirs2remove = self.ch.getOutputString().split("\n")
                    #####
                    # Reverse list as list is generated with parents first
                    # rather than children first.
                    dirs2remove.reverse()
                    self.logger.log(LogPriority.DEBUG, dirs2remove)
                    if str(self.ch.getReturnCode()) == str(0):
                        for dir in dirs2remove:
                            if dir:
                                try:
                                    #####
                                    # Make sure "/" is prepended to the directory
                                    # tree as pkgutil does not report the first "/"
                                    # in the file path
                                    os.rmdir(install_root + dir)
                                    #####
                                    # We don't care if any of the child directories
                                    # still have files, as directories such as
                                    # /usr/bin, /usr/local/bin are reported by
                                    # pkgutil in the directory listing, which is
                                    # why we use os.rmdir rather than shutil.rmtree
                                    # and we don't report on the success or failure
                                    # of removing directories.
                                except OSError as err:
                                    self.logger.log(LogPriority.DEBUG,
                                                    "Error trying to remove: "
                                                    + str(dir))
                                    self.logger.log(LogPriority.DEBUG,
                                                    "With Exception: " +
                                                    str(err))
                                    pass

                        #####
                        # Make the system package database "forget" the package
                        # was installed.
                        cmd_three = ["/usr/sbin/pkgutil", "--forget", domain]
                        self.ch.executeCommand(cmd_three)
                        if re.match("^%s$" %
                                    str(self.ch.getReturnCode()).strip(),
                                    str(0)):
                            success = True
                else:
                    self.logger.log(LogPriority.DEBUG, "Page: \"" +
                                    str(package) + "\" Not found")

        except(KeyboardInterrupt, SystemExit):
            raise
        except Exception as err:
            print(err)
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.DEBUG, self.detailedresults)
        if success:
            try:
                self.libc.sync()
            except:
                pass
        print(("Remove Package success: " + str(success)))
        return success

    ###########################################################################

    def checkInstall(self, package):
        '''Check the installation status of a package. Return a bool; True if
        the package is installed.
        
        Use pkgutil to determine if package has been installed or not.

        :param string: package : Name of the package whose installation status
            is to be checked, must be recognizable to the underlying package
            manager.
        :param package: 
        :returns: bool :
        @author: rsn

        '''
        success = False
        self.package = package
        try:
            #####
            # Perform a reverse lookup to get the domain...
            domain = self.findDomain(package)
            self.logger.log(LogPriority.DEBUG, "Domain: " + str(domain))
            if domain:
                success = True
                self.logger.log(LogPriority.DEBUG,
                                "Domain: " + str(domain) + " found")

        except(KeyboardInterrupt, SystemExit):
            raise
        except Exception as err:
            print(err)
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.DEBUG, self.detailedresults)

        return success

    ###########################################################################

    def checkAvailable(self, package):
        '''Check if a package is available at the "reporoot"

        :param package: 
        :returns: success
        :rtype: bool
@author: Roy Nielsen
@change: Breen Malmberg - 2/28/2017 - added logic to handle case where
        self.reporoot is undefined; added logging; minor doc string edit

        '''

        success = False
        self.package = package

        if not self.reporoot:
            self.logger.log(LogPriority.WARNING, "No MacRepoRoot defined! Unable to determine package URL!")
            return success

        try:

            self.logger.log(LogPriority.DEBUG, "Checking if: " +
                            str(package)) + " is available on the server..."
            self.logger.log(LogPriority.DEBUG, "From repo: " +
                            str(self.reporoot))

            self.pkgUrl = self.reporoot + "/" + package

            # If there network, install, else no network, log
            if self.connection.isPageAvailable(self.pkgUrl):
                self.logger.log(LogPriority.DEBUG, "There is a connection to" +
                                                   " the server...")
                #####
                # Download the file
                self.downloadPackage()

                #####
                # Perform a md5 checksum - if there is a match, return
                # success = True
                if self.checkMd5():
                    success = True
        except(KeyboardInterrupt, SystemExit):
            raise
        except Exception as err:
            print(err)
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.DEBUG, self.detailedresults)

        return success

    ###########################################################################

    def getInstall(self, package):
        return self.installPackage(package)

    ###########################################################################

    def getRemove(self, package):
        return self.removePackage(package)

    ###########################################################################

    def findDomain(self, pkg=""):
        '''Go through the package receipts database to find a package, and return
        a domain.  Apple stores package information in "domain" format, rather
        than a package name format. Accessing the package name means we need to
        look through all the ".plist" files in /var/db/receipts to find the
        package name, then we can return the domain so that can be used for
        package management.
        
        Install package receipts can be found in /var/db/receipts.
        
        A domain is the filename in the receipts database without the ".plist"
        or ".bom".
        
        An example is org.macports.MacPorts

        :param eters: pkg - the name of the install package that we need the
                     domain for.
        :param pkg:  (Default value = "")
        :returns: s: domains - the first domain in a possible list of domains.
        
        @author: Roy Nielsen

        '''
        try:
            self.logger.log(LogPriority.DEBUG, "Looking for: " + str(pkg))
            path = "/var/db/receipts/"
            files = []
            domain = ""
            for name in os.listdir(path):
                if os.path.isfile(os.path.join(path, name)) and \
                   os.path.isfile(os.path.join(path, name)) and \
                   name.endswith(".plist"):
                    files.append(name)

            unwrap = "/usr/bin/plutil -convert xml1 /var/db/receipts/*.plist"
            wrap = "/usr/bin/plutil -convert binary1 /var/db/receipts/*.plist"

            self.ch.executeCommand(unwrap)

            if not re.match("^%s$" % str(self.ch.getReturnCode()), str(0)):
                #####
                # Unwrap command didn't work... return None
                domain = None
            else:
                try:
                    self.libc.sync()
                except:
                    pass
                #####
                # Unwrap command worked, process the receipt plists
                for afile in files:
                    if re.match("^\..+.plist", afile):
                        continue
                    self.logger.log(LogPriority.DEBUG, "afile: " + str(afile))
                    #####
                    # Get the path without the plist file extension.
                    afile_path = os.path.join(path, afile)
                    #####
                    # Make sure we have a valid file on the filesystem
                    if os.path.isfile(afile_path):
                        try:
                            plist = plistlib.readPlist(afile_path)
                        except Exception as err:
                            self.logger.log(LogPriority.DEBUG, "Exception " +
                                                               "trying to use" +
                                                               " plistlib: " +
                                                               str(err))
                            raise err
                        else:
                            if re.match("^%s$" % plist['PackageFileName'],
                                        pkg):
                                #####
                                # Find the first instance of the
                                # PackageFileName without the .plist.
                                domain = ".".join(afile.split(".")[:-1])
                                break
                #####
                # Make the plists binary again...
                self.ch.executeCommand(wrap)

                #####
                # Log the domain...
                self.logger.log(LogPriority.DEBUG, "Domain: " + str(domain))
            print(("findDomain: " + str(domain)))
        except(KeyboardInterrupt, SystemExit):
            raise
        except Exception as err:
            print(err)
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.DEBUG, self.detailedresults)

        return domain

    ###########################################################################

    def downloadPackage(self):
        '''Download the package in the self.package URL to a temporary directory
        created by tempfile.mkdtemp.  Does not checked for a cached file.
        
        @Note: path to downloaded file will be in the self.tmpLocalPkg variable
               for use by other class methods.
        
        @author: Roy Nielsen


        '''
        success = False
        urlfile = None
        try:
            self.tmpDir = ""
            if self.pkgUrl:
                #####
                # Make a temporary directory to download the package
                try:
                    self.tmpDir = tempfile.mkdtemp()
                    self.logger.log(LogPriority.DEBUG, "tmpDir: " +
                                                       str(self.tmpDir))
                except Exception as err:
                    message = "Problem creating temporary directory: " + \
                              str(err)
                    self.logger.log(LogPriority.WARNING, message)
                    raise err
                else:
                    #####
                    # First try to open the URL
                    if self.connection.isPageAvailable(self.pkgUrl):
                        urlfile = urllib.request.urlopen(self.pkgUrl, timeout=10)
                        #####
                        # Get just the package name out of the "package" url
                        urlList = self.pkgUrl.split("/")
                        self.pkgName = urlList[-1]
                        self.tmpLocalPkg = os.path.join(self.tmpDir,
                                                        self.pkgName)
                        try:
                            #####
                            # Next try to open a file for writing the local file
                            f = open(str(self.tmpLocalPkg).strip(), "w")
                        except IOError as err:
                            message = "Error opening file - err: " + str(err)
                            self.logger.log(LogPriority.INFO, message)
                            raise err
                        except Exception as err:
                            message = "Generic exception opening file - err: " + \
                                      str(err)
                            self.logger.log(LogPriority.INFO, message)
                            raise err
                        else:
                            self.logger.log(LogPriority.DEBUG,
                                            "..................")
                            #####
                            # take data out of the url stream and put it in the
                            # file a chunk at a time - more sane for large files
                            chunk = 16 * 1024 * 1024
                            counter = 0
                            while 1:
                                try:
                                    if urlfile:
                                        databytes = urlfile.read(chunk)
                                        if not databytes:
                                            message = "Done reading file: " + \
                                                      self.pkgUrl
                                            self.logger.log(LogPriority.DEBUG,
                                                            message)
                                            break
                                        data = databytes
                                        f.write(data.decode('utf-8', 'replace'))
                                        message = "Read " + str(len(databytes)) + \
                                            " bytes"
                                        self.logger.log(LogPriority.DEBUG,
                                                        message)
                                    else:
                                        raise IOError("What file???")
                                except (OSError or IOError) as err:
                                    #####
                                    # Catch in case we run out of space on the
                                    # local system during the download process
                                    message = "IO error: " + str(err)
                                    self.logger.log(LogPriority.INFO, message)
                                    raise err
                                except SSLError:
                                    # This will catch timeouts. Due to a bug in
                                    # the urlfile object, it will sometimes not
                                    # read, and will need to be recreated. The
                                    # counter ensures that if the error is not
                                    # a simple timeout, it will not get stuck
                                    # in an infinite loop.
                                    counter += 1
                                    if counter > 3:
                                        raise
                                    self.logger.log(LogPriority.DEBUG,
                                                    "Connection timed out. " +
                                                    "Creating new urlfile " +
                                                    "object.")
                                    urlfile = urllib.request.urlopen(self.pkgUrl,
                                                              timeout=10)
                                else:
                                    success = True
                            f.close()
                        urlfile.close()
                    else:
                        message = "Error opening the URL: " + str(self.pkgUrl)
                        self.logger.log(LogPriority.DEBUG, message)
            else:
                message = "Need a valid package url. Can't download nothing..."
                self.logger.log(LogPriority.DEBUG, message)
        except(KeyboardInterrupt, SystemExit):
            raise
        except Exception as err:
            print(err)
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.INFO, self.detailedresults)
        return success

    ###########################################################################

    def checkMd5(self):
        '''Validate that the MD5 of the file is the same as the one on the server,
        for consistency's sake, ie we got a good download.
        
        Takes an MD5 of the local file, then appends it to the filename with a
        ".", then does a request and getreqpose and checks for a "200 in the
        response.status field.
        
        .<filename>.<UPPER-md5sum>
        
        Specific to the Casper server for Macs.  If you want to override this
        method for another OS, subclass this class, or rewrite the function.
        
        @author: Roy Nielsen


        '''
        success = False
        try:
            hashSuccess, myhash = self.getDownloadedFileMd5sum()
            if hashSuccess and myhash:
                #####
                # Generate the name of the hash using the generated md5
                hashname = "." + self.package + \
                           "." + str(myhash).upper().strip()

                message = "Hashname: " + str(hashname).strip()
                self.logger.log(LogPriority.DEBUG, message)

                #####
                # Generate a web link to the hash file
                hashUrlList = self.pkgUrl.split("/")
                del hashUrlList[-1]
                hashUrlList.append(str(hashname))
                self.hashUrl = "/".join(hashUrlList)

                self.logger.log(LogPriority.DEBUG, "Page: " + self.hashUrl)

                if self.connection.isPageAvailable(self.hashUrl):
                    success = True
                    message = "Found url: " + str(self.hashUrl)
                else:
                    message = "Did NOT find url: " + str(self.hashUrl)
                self.logger.log(LogPriority, message)
        except(KeyboardInterrupt, SystemExit):
            raise
        except Exception as err:
            print(err)
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.INFO, self.detailedresults)

        return success

    ###########################################################################

    def getDownloadedFileMd5sum(self):
        '''Get the md5sum of a file on the local filesystem.  Calculate the data
        in chunks in case of large files.


        :returns: s: retval - the md5sum of the file, or -1 if unsuccessful in
                           getting the md5sum
        
        @author: Roy Nielsen

        '''
        success = False
        try:
            retval = None
            try:
                if os.path.exists(self.tmpLocalPkg):
                    fh = open(self.tmpLocalPkg, 'r')
                else:
                    raise Exception("Cannot open a non-existing file...")
            except Exception as err:
                message = "Cannot open file: " + self.tmpLocalPkg + \
                    " for reading."
                self.logger.log(LogPriority.WARNING, message)
                self.logger.log(LogPriority.WARNING, "Exception : " + str(err))
                success = False
                retval = '0'
            else:
                chunk = 16 * 1024 * 1024
                m = hashlib.md5()
                while True:
                    data = fh.read(chunk).encode('utf-8')
                    if not data:
                        break
                    m.update(data)
                retval = m.hexdigest()

        except(KeyboardInterrupt, SystemExit):
            raise
        except Exception as err:
            print(err)
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.INFO, self.detailedresults)
        else:
            success = True

        return success, str(retval).strip()

    ###########################################################################

    def unArchive(self):
        '''Unarchive tar, tar.gz, tgz, tar.bz, and zip files.  Using tarfile and
        zipfile libraries
        
        @Note: Will look for the first "*.app" or "*.pkg", and use that as the
               self.tmpLocalPkg that the rest of the class will use to try to
               install.  This way the compressed file can be named something
               different than the actual package to install.  This means that
               the compressed package can be named generically and a specified
               version is in the compressed file.
        
               Caution - This means when removing the package means you need to
               know the name of the package inside the compressed file to
               remove the installed file.
        
        @author: Roy Nielsen


        '''
        try:
            retval = 0
            ##############################################
            # Check if the file is a tar file of some sort
            inTarList = False
            tars = ["tar", "tgz", "tar.gz", "tbz", "tar.bz"]
            for extension in tars:
                if filename.endswith(extension):
                    inTarList = True

            if re.match("^\s*$", filename):
                self.logger.log(LogPriority.DEBUG,
                                "Emtpy archive name, cannot uncompress.")
                retval = -1
            elif inTarList:
                import tarfile
                try:
                    #####
                    # Open the tar file to prepare for extraction
                    tar = tarfile.open(filename)
                    try:
                        message = "file: " + filename + ", dest: " + destination
                        self.logger.log(LogPriority.DEBUG, message)
                        #####
                        # Extract the tarball at the "destination" location
                        tar.extractall(destination)
                    except (OSError | IOError) as err:
                        message = "Error extracting archive: " + str(err)
                        self.logger.log(LogPriority.WARNING, message)
                        raise err
                except Exception as err:
                    message = "Error opening archive: " + str(err)
                    self.logger.log(LogPriority.DEBUG, message)
                    raise err
                else:
                    tar.close()
                    retval = 0

            elif filename.endswith("zip"):
                ###############################################
                # Process if it a zip file.
                #
                # partial reference at:
                # http://stackoverflow.com/questions/279945/set-permissions-on-a-compressed-file-in-python
                import zipfile
                #####
                # create a zipfile object, method for python 2.5:
                # http://docs.python.org/release/2.5.2/lib/module-zipfile.html
                # Use this type of method as without it the unzipped
                # permissions aren't what they are supposed to be.
                uzfile = zipfile.ZipFile(filename, "r")
                if zipfile.is_zipfile(filename):
                    #####
                    # if the file is a zipfile
                    #
                    # list the files inside the zipfile
                    # for internal_filename in uzfile.namelist():
                    for ifilename in uzfile.filelist:
                        perm = ((ifilename.external_attr >> 16) & 0o777)
                        internal_filename = ifilename.filename
                        message = "extracting file: " + str(internal_filename)
                        self.logger.log(LogPriority.DEBUG, message)
                        #####
                        # Process directories first
                        if internal_filename.endswith("/"):
                            #####
                            # if a directory is found, create it (zipfile
                            # doesn't)
                            try:
                                os.makedirs(os.path.join(destination,
                                                         internal_filename.strip("/")),
                                            perm)
                            except OSError as err:
                                message = "Error making directory: " + str(err)
                                self.logger.log(LogPriority.DEBUG, message)
                                raise err
                            else:
                                continue
                        #####
                        # Now process if it is not a directoy
                        try:
                            contents = uzfile.read(internal_filename)
                        except RuntimeError as err:
                            #####
                            # Calling read() on a closed ZipFile will raise a
                            # RuntimeError
                            self.logger.log(LogPriority.DEBUG,
                                            ["InstallingHelper.un_archive",
                                             "Called read on a closed ZipFile: "
                                             + str(err)])
                            raise err
                        else:
                            try:
                                #####
                                # using os.open as regular open doesn't give
                                # the ability to set permissions on a file.
                                unzip_file = os.path.join(destination,
                                                          internal_filename)
                                target = os.open(unzip_file,
                                                 os.O_CREAT | os.O_WRONLY,
                                                 perm)
                                try:
                                    #####
                                    # when an exception is thrown in the try block, the
                                    # execution immediately passes to the finally block
                                    # After all the statements in the finally block are
                                    # executed, the exception is raised again and is 
                                    # handled in the except statements if present in
                                    # the next higher layer of the try-except statement
                                    os.write(target, contents)
                                finally:
                                    os.close(target)
                            except OSError as err:
                                message = "Error opening file for writing: " + \
                                          str(err)
                                self.logger.log(LogPriority.WARNING, message)
                                raise err
                    uzfile.close()
                    if not retval < -1:
                        # if there was not an error creating a directory as
                        # part of the unarchive process
                        retval = 0
                else:
                    # Not a zipfile based on the file's "magic number"
                    self.logger.log(LogPriority.DEBUG,
                                    ["InstallingHelper.un_archive",
                                     "file: " + filename +
                                     " is not a zipfile."])
                    retval = -1
            #####
            # Find the first item in the directory that is a .app | .pkg | .mpkg
            # Use that as the self.tmpLocalPkg that the rest of the class will
            # use to try to install.  This way the compressed file can be
            # named something different than the actual package to install.
            # This means that the compressed package can be named generically
            # and a specified version is in the compressed file.
            # Caution - This means when removing the package means you need
            # to know the name of the package inside the compressed file to
            # remove the installed file.
            dirlist = os.listdir(self.tmpDir)
            for myfile in dirlist:
                if re.search(".app", myfile) or \
                   re.search(".pkg", myfile) or \
                   re.search(".mpkg", myfile):
                    self.tmpLocalPkg = self.tmpDir + "/" + str(myfile)
                    break

        except(KeyboardInterrupt, SystemExit):
            raise
        except Exception as err:
            print(err)
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.INFO, self.detailedresults)

        return retval

    ###########################################################################

    def copyInstall(self, dest="/Applications", isdir=True, mode=0o775,
                    owner="root", group="admin"):
        '''Copies a directory tree, or contents of a directory to "dest"
        destination.

        :param dest:  (Default value = "/Applications")
        :param isdir:  (Default value = True)
        :param mode:  (Default value = 0775)
        :param owner:  (Default value = "root")
        :param group:  (Default value = "admin")

        '''
        try:
            if re.match("^\s*$", self.tmpDir):
                self.tmpDir = "."

            src_path = self.tmpDir + "/" + self.pkgName

            self.logger.log(LogPriority.DEBUG, "New src_path: " + src_path)

            if isdir:
                try:
                    shutil.copytree(src_path, dest)
                except Exception as err:
                    message = "Unable to recursively copy dir: " + src_path + \
                              " error: " + str(err)
                    self.logger.log(LogPriority.ERROR, message)
                    raise Exception(message)
                else:
                    success = True
                    #####
                    # try to chmod the directory (not recursively)
                    try:
                        os.chmod(dest, mode)
                    except OSError as err:
                        success = False
                        message = "Unable to change mode: " + str(err)
                        self.logger.log(LogPriority.ERROR, message)
                        raise Exception(message)
                    #####
                    # UID & GID needed to chown the directory
                    uid = 0
                    gid = 80
                    try:
                        uid = pwd.getpwnam(owner)[2]
                    except KeyError as err:
                        success = False
                        message = "Error: " + str(err)
                        self.logger.log(LogPriority.DEBUG, message)
                        raise Exception(message)
                    try:
                        gid = grp.getgrnam(group)[2]
                    except KeyError as err:
                        success = False
                        message = "Error: " + str(err)
                        self.logger.log(LogPriority.DEBUG, message)
                        raise Exception(message)
                    #####
                    # Can only chown if uid & gid are int's
                    if ((type(uid) == type(int())) and
                        (type(gid) == type(int()))):
                        try:
                            os.chown(dest, uid, gid)
                        except OSError as err:
                            success = False
                            message = "Error: " + str(err)
                            self.logger.log(LogPriority.DEBUG, message)
                            raise Exception(message)
            else:
                try:
                    dir_list = os.listdir(src_path)
                except OSError as err:
                    message = "Error listing files from: " + src_path + \
                              " error: " + str(err)
                    self.logger.log(LogPriority.ERROR, message)
                    raise Exception(message)
                else:
                    for myfile in dir_list:
                        try:
                            shutil.copy2(myfile, dest)
                        except Exception as err:
                            message = "Error copying file: " + myfile + \
                                      " error: " + str(err)
                            self.logger.log(LogPriority.DEBUG, message)
                            raise
                        else:
                            success = True
        except(KeyboardInterrupt, SystemExit):
            raise
        except Exception as err:
            print(err)
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.INFO, self.detailedresults)

        return success

    ##########################################################################

    def installPkg(self):
        '''This function references generic methods from the InstallingHelper
        class to download an archived .pkg or .mpkg file, check md5sum of
        the file against the md5 on the server, unarchive the file and
        install the package. It will remove the temporary directory that
        the file was downloaded and unarchived to when the install is
        complete.
        
        local vars:
        tmp_dir = the temporary directory that is created for the downloaded
                  file, and to unarchive it into.
        self.sig_match = A variable that tells if the signature of the
                         downloaded file and the server string match.  Set
                         in the download_and_prepare method of the parent
                         class
        pkg_extension = for verifying that the package extension is either
                        ".pkg" or ".mpkg"
        self.package_name = the name of the package to be downloaded, without
                            the archive extension.  Set in the InstallingHelper
                            class.
        
        @author: Roy Nielsen


        '''
        success = False
        try:
            #####
            # Check if it's a pkg or mpkg
            if self.tmpLocalPkg.endswith(".pkg") or \
               self.tmpLocalPkg.endswith(".mpkg"):
                #####
                # set up the install package command string
                cmd = ["/usr/bin/sudo", "/usr/sbin/installer", "-verboseR",
                       "-pkg", self.tmpLocalPkg, "-target", "/"]
                self.ch.executeCommand(cmd)
                self.logger.log(LogPriority.DEBUG,
                                self.ch.getOutputString())
                if self.ch.getReturnCode() == 0:
                    success = True
                    #####
                    # remove the temporary directory where the archive was
                    # downloaded and unarchived.
                    try:
                        shutil.rmtree(self.tmpDir)
                    except Exception as err:
                        self.logger.log(LogPriority.ERROR,
                                        "Exception: " + str(err))
                        raise err
                    else:
                        success = True
            else:
                self.logger.log(LogPriority.ERROR, "Valid package " +
                                "extension not found, cannot install:" +
                                " " + self.tmpLocalPkg)
        except(KeyboardInterrupt, SystemExit):
            raise
        except Exception as err:
            print(err)
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.INFO, self.detailedresults)

        return success
Beispiel #3
0
class Freebsd(object):
    '''The template class that provides a framework that must be implemented by
    all platform specific pkgmgr classes.
    
    :version:
    @author: Derek T Walker 08-06-2012


    '''

    def __init__(self, logger):
        self.logger = logger
        self.ch = CommandHelper(self.logger)
        self.install = "/usr/sbin/pkg_add -r -f "
        self.remove = "/usr/sbin/pkg_delete "
        self.info = "/usr/sbin/pkg_info "
        self.versioncheck = "/usr/sbin/pkg_version -l < "

###############################################################################
    def installpackage(self, package):
        '''Install a package. Return a bool indicating success or failure.

        :param string: package : Name of the package to be installed, must be
                recognizable to the underlying package manager.
        :param package: 
        :returns: installed
        :rtype: bool
@author: Derek T. Walker
@change: Breen Malmberg - 4/18/2017 - doc string fixes; refactor
        of method; parameter validation

        '''

        installed = False

        try:

            # parameter validation
            if not package:
                self.logger.log(LogPriority.DEBUG, "Parameter: package was blank!")
                return installed
            if not isinstance(package, str):
                self.logger.log(LogPriority.DEBUG, "Parameter: package needs to be of type string. Got: " + str(type(package)))
                return installed

            self.ch.executeCommand(self.install + package)

            if self.ch.getReturnCode() == 0:
                self.logger.log(LogPriority.DEBUG, "Package " + str(package) + " installed successfully")
                installed = True
            else:
                self.logger.log(LogPriority.DEBUG, "Failed to install package " + str(package))

        except Exception:
            raise
        return installed

###############################################################################
    def removepackage(self, package):
        '''Remove a package. Return a bool indicating success or failure.

        :param package: string; Name of the package to be removed, must be
                recognizable to the underlying package manager.
        :returns: removed
        :rtype: bool
@author: Derek T. Walker
@change: Breen Malmberg - 4/18/2017 - refactor of method; doc string fixes;
        parameter validation

        '''

        removed = True

        try:

            # parameter validation
            if not package:
                self.logger.log(LogPriority.DEBUG, "Parameter: package was blank!")
                return removed
            if not isinstance(package, str):
                self.logger.log(LogPriority.DEBUG, "Parameter: package needs to be of type string. Got: " + str(type(package)))
                return removed

            self.ch.executeCommand(self.remove + package)
            retcode = self.ch.getReturnCode()
            if retcode != 0:
                self.logger.log(LogPriority.DEBUG, "Failed to remove package " + str(package))
                removed = False
            else:
                self.logger.log(LogPriority.DEBUG, "Successfully removed package " + str(package))

        except Exception:
            raise
        return removed

###############################################################################
    def checkInstall(self, package):
        '''Check the installation status of a package. Return a bool; True if
        the package is installed.

        :param package: string; Name of the package whose installation status
                is to be checked, must be recognizable to the underlying package
                manager.
        :returns: installed
        :rtype: bool
@author: Derek T. Walker
@change: Breen Malmberg - 4/18/2017 - refactor of method; doc
        string fixes; parameter validation

        '''

        installed = False

        try:

            # parameter validation
            if not package:
                self.logger.log(LogPriority.DEBUG, "Parameter: package was blank!")
                return installed
            if not isinstance(package, str):
                self.logger.log(LogPriority.DEBUG, "Parameter: package needs to be of type string. Got: " + str(type(package)))
                return installed

            self.ch.executeCommand(self.info + package)
            retcode = self.ch.getReturnCode()
            if retcode != 0:
                self.logger.log(LogPriority.DEBUG, "Package " + str(package) + " is NOT installed")
            else:
                self.logger.log(LogPriority.DEBUG, "Package " + str(package) + " is installed")
                installed = True

        except Exception:
            raise
        return installed

    def checkUpdate(self, package=""):
        '''STUB METHOD
        check for updates for the specified package
        if package is not specified, check for all
        package updates
        
        currently unfinished as I have no earthly idea
        how to reliably manage packages on bsd
        (ports? portsmanager? portsmaster? pkg_zzz? pkg?)
        also versioning heavily affects what package manager
        binary(ies) is/are available.

        :param package: string; name of package to check (Default value = "")
        :returns: updatesavail
        :rtype: bool
@author: Breen Malmberg

        '''

        updatesavail = False

        try:

            pass # stub

        except Exception:
            raise
        return updatesavail

    def Update(self, package=""):
        '''STUB METHOD
        update the specified package
        if no package is specified, then update
        all packages on the system
        
        currently unfinished as I have no earthly idea
        how to reliably manage packages on bsd
        (ports? portsmanager? portsmaster? pkg_zzz? pkg?)
        also versioning heavily affects what package manager
        binary(ies) is/are available.

        :param package: string; name of package to update (Default value = "")
        :returns: updated
        :rtype: bool
@author: Breen Malmberg

        '''

        updated = False

        try:

            pass # stub

        except Exception:
            raise
        return updated

    def getPackageFromFile(self, filename):
        '''return a string containing the name of the package
        which provides the specified filename

        :param filename: 
        :returns: packagename
        :rtype: string
@author: Breen Malmberg

        '''

        packagename = ""

        try:

            # parameter validation
            if not filename:
                self.logger.log(LogPriority.DEBUG, "Parameter: filename was blank!")
                return packagename
            if not isinstance(filename, str):
                self.logger.log(LogPriority.DEBUG, "Parameter: filename needs to be of type string. Got: " + str(type(filename)))
                return packagename

            self.ch.executeCommand(self.info + " -W " + filename)
            if self.ch.getReturnCode() == 0:
                packagename = self.ch.getOutputString()
            else:
                self.logger.log(LogPriority.DEBUG, "Failed to get the package for the given filename")

        except Exception:
            raise
        return packagename

###############################################################################
    def getInstall(self):
        return self.install

###############################################################################
    def getRemove(self):
        return self.remove

    def getInfo(self):
        return self.info
Beispiel #4
0
class KVAProfiles(object):
    def __init__(self, logger, path):
        self.logger = logger
        self.path = path
        self.undocmd = ""
        self.installcmd = ""
        self.data = ""
        self.badvalues = ""

    def validate(self, output, data):
        '''@summary: Method checks if either profile is installed and/or contents of
        profile is up to par with our security standards
        @author: dwalker
        :param output: The output from system_profiler
            SPConfigurationProfileDataType command
        :param data: The data dictionary from the calling rule
            with the profile identifiers and key value pairs
            to look for. View KVEditor class for details on data dict format
        :returns: bool - True or False
        '''
        '''Go throught output and see if we can find the profile
        identifier (key)'''
        self.badvalues = ""
        self.data = data
        for key, val in self.data.items():
            '''In some cases val will be blank in which case we're just
                    looking for the presence of the profile or more specifically
                    the identifier (key)'''
            if not val:
                for line in output:
                    if re.search("^" + key, line.strip()):
                        return True
                '''We never found the profile, return False'''
                debug = "The profile sub-identifier:" + key + " was not found\n"
                self.logger.log(LogPriority.DEBUG, debug)
                return False
            iterator1 = 0
            for line in output:
                keyoutput = []
                '''We found the profile identifier'''
                if re.search("^" + key + ":$", line.strip()):
                    ''''Put all output after the identifier line into a 
                    new list called temp'''
                    temp = output[iterator1 + 1:]
                    iterator2 = 0
                    '''Go through this new list and look for where the 
                    payload section starts.  The rest of the output gets
                    stored in the keyoutput list'''
                    for line in temp:
                        if re.search("^Payload Data:", line.strip()):
                            temp = temp[iterator2 + 1:]
                            keyoutput = temp
                            break
                        else:
                            iterator2 += 1
                    if keyoutput:
                        payloadblocktemp = []
                        '''This next loop is getting everything inside the payload
                        section stopping before the next identifier'''
                        for line in keyoutput:
                            if not re.search(".*:", line):
                                line = re.sub("\s+", "", line)
                                payloadblocktemp.append(line)
                            else:
                                break
                        payloadblock = []
                        i = 0
                        dontadd = False
                        '''This loop is to clean up the output and squash together
                        any consecutive lines that are blank values inside {} or ()'''
                        while i < len(payloadblocktemp):
                            if dontadd:
                                i += 1
                                dontadd = False
                                continue
                            '''The next two if statements check to see if two consecutive
                            lines represent an empty dictionary or tuple.  If so we want
                            to combine these into one line without the ; at the end'''
                            if re.search("\{$", payloadblocktemp[i]):
                                try:
                                    if re.search("\};$",
                                                 payloadblocktemp[i + 1]):
                                        dontadd = True
                                        line = re.sub(";$", "",
                                                      payloadblocktemp[i + 1])
                                        payloadblock.append(
                                            payloadblocktemp[i] + line)
                                    else:
                                        payloadblock.append(
                                            payloadblocktemp[i])
                                except IndexError as err:
                                    debug = "File in bad format, fix will install profile\n"
                                    self.logger.log(LogPriority.DEBUG,
                                                    [debug, err])
                                    return False
                            elif re.search("\($", payloadblocktemp[i]):
                                try:
                                    if re.search("\);$",
                                                 payloadblocktemp[i + 1]):
                                        dontadd = True
                                        line = re.sub(";$", "",
                                                      payloadblocktemp[i + 1])
                                        payloadblock.append(
                                            payloadblocktemp[i] + line)
                                    else:
                                        payloadblock.append(
                                            payloadblocktemp[i])
                                except IndexError as err:
                                    debug = "File in bad format, fix will install profile\n"
                                    self.logger.log(LogPriority.DEBUG,
                                                    [debug, err])
                                    return False
                            else:
                                payloadblock.append(payloadblocktemp[i])
                            i += 1
                        '''k is the key inside val variable (e.g. allowsimple)
                        and v is the value, in this example, a dict (e.g. {"val": "1",
                                                                           "type": "bool",
                                                                           "accept": "",
                                                                           "result": False"})'''
                        for k, v in val.items():
                            retval = False
                            if isinstance(v["val"], list):
                                retval = self.checkTuple(k, v, payloadblock)
                            elif isinstance(v["val"], dict):
                                retval = self.checkDict(k, v, payloadblock)
                            else:
                                retval = self.checkSimple(k, v, payloadblock)
                            if retval == True:
                                v["result"] = True
                else:
                    iterator1 += 1
        self.setbadvalues()
        return self.data

    def checkSimple(self, k, v, payloadblock):
        '''@summary: Method that checks payloadblock contents
                for k (key) and associated v (value)
        @author: dwalker
        :param k: Not to be confused with the key in the calling
                method which was our identifier before the payload.
                This key is now the key in the inner dictionary
                passed through as val from the calling method
        :param v: Not to be confused with the value in the calling
                method which was our inner dictionary passed through
                as val. This val is now the inner dictionary:
                {"val":...,
                 "type": ...,
                 "accept": ...,
                 "result": False}
        :param payloadblock: A list of lines from our payload
                portion of the output from the system_profiler
                command
        :returns: bool - True or False
        '''
        founditem = False
        retval = True
        unsecure = False
        debug = ""
        for line in payloadblock:
            if re.search("^\"{0,1}" + k + "\"{0,1}=", line.strip()):
                founditem = True
                temp = line.strip().split("=")
                try:
                    if temp[1]:
                        '''Remove any arbitrary whitespace'''
                        temp[1] = re.sub("\s", "", temp[1])
                        '''Remove semicolon at end if exists'''
                        temp[1] = re.sub(";$", "", temp[1])
                        '''We handle strings and bools the same, checking the value
                        and if accept key has an alternately accpeted value, we 
                        check for that, if neither match, return False'''
                        if v["type"] == "bool" or v["type"] == "string":
                            if str(temp[1].strip()) != v["val"]:
                                if v["accept"]:
                                    if temp[1].strip() != v["accept"]:
                                        debug += "Key: " + k + " doesn't " + \
                                                 "contain the correct boolean " + \
                                                 "value\n"
                                        unsecure = True
                                        break
                                else:
                                    debug += "Key: " + k + " doesn't " + \
                                             "contain the correct boolean " + \
                                             "value\n"
                                    unsecure = True
                                    break
                            '''If the second value inside the list v is the word
                            int, then we want to make sure that our value found
                            after the = in our output matches what we're expecting
                            in v["val"] which could be any numerical integer.
                            It it's not correct, then we check the accept key for the
                            word "more" or "less".  More indicates that if the values
                            don't match but the value in our output is a greater 
                            integer value than what we're expecting, then it's still
                            ok and vice versa with the less keyword.'''
                        elif v["type"] == "int":
                            if temp[1].strip() != v["val"]:
                                if v["accept"] == "more":
                                    if int(temp[1].strip()) < int(v["val"]):
                                        debug += "Key: " + k + " doesn't " + \
                                                 "contain the correct integer " + \
                                                 "value\n"
                                        unsecure = True
                                        break
                                elif v["accept"] == "less":
                                    if int(temp[1].strip()) > int(v["val"]):
                                        debug += "Key: " + k + " doesn't " + \
                                                 "contain the correct integer " + \
                                                 "value\n"
                                        unsecure = True
                                        break
                                elif v["accept"] == "":
                                    debug = "Key: " + k + " doesn't " + \
                                            "contain the correct integer " + \
                                            "value\n"
                                    unsecure = True
                                else:
                                    error = "Invalid value for accept parameter in data " + \
                                            "dictionary. Needs to be either more or less keyword\n"
                                    self.logger.log(LogPriority.ERROR, error)
                                    raise Exception(error)
                except IndexError:
                    debug += "Profile in bad format\n"
                    break
        if not founditem:
            debug = "Key: " + k + " not found\n"
            retval = False
        if unsecure:
            debug = "Key: " + k + " found but had an incorrect value\n"
            retval = False
        if debug:
            self.logger.log(LogPriority.DEBUG, debug)
        return retval

    def checkTuple(self, k, v, payloadblock):
        '''@summary: Method that checks payloadblock contents
                for k (key) and associated v (value)
        @author: dwalker
        :param k: Not to be confused with the key in the calling
                method which was our identifier before the payload.
                This key is now the key in the inner dictionary
                passed through as val from the calling method
        :param v: Not to be confused with the value in the calling
                method which was our inner dictionary passed through
                as val. This val is now the inner dictionary:
                {"val":...,
                 "type": ...,
                 "accept": ...,
                 "result": False}
        :param payloadblock: A list of lines from our payload
                portion of the output from the system_profiler
                command
        :returns: bool - True or False
        '''
        retval = True
        iterator = 0
        temp, temp2 = [], []
        for line in payloadblock:
            if re.search("^\"{0,1}" + k + "\"{0,1}=", line.strip()):
                if re.search("\(\)$", line):
                    if str(v["val"]) == "[]":
                        return True
                    else:
                        return False
                elif re.search("\($", line):
                    temp = payloadblock[iterator + 1:]
                    break
            else:
                iterator += 1
        iterator = 0
        for line in temp:
            if re.search("\)\;", line):
                temp2 = temp[:iterator]
            else:
                iterator += 1
        if temp2:
            temp = temp2
        if temp:
            replaceables = []
            for line in temp:
                if re.search("\,$", line):
                    line = re.sub("\,$", "", line)
                replaceables.append(line)
            temp = replaceables
            removeables = []
            for line in temp:
                if line in v["val"]:
                    removeables.append(line)
            if removeables:
                v = v["val"]
                for item in removeables:
                    v.remove(item)
                    temp.remove(item)
                v = tuple(v)
            if v:
                '''There are still items left so we didn't find them all'''
                debug = "The following tuple items weren't found for the key " + k + "\n"
                debug += str(v) + "\n"
                self.logger.log(LogPriority.DEBUG, debug)
                retval = False
            if temp:
                debug = "The following items were in the output that shouldn't have been\n"
                for item in temp:
                    if not re.search("\)\;|\($", item):
                        debug += str(temp) + "\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                        retval = False
        else:
            debug = "key " + k + " wasn't found\n"
            self.logger.log(LogPriority.DEBUG, debug)
            retval = False
        return retval

    def checkDict(self, k, v, payloadblock):
        '''@summary: Method that checks payloadblock contents
                for k (key) and associated v (value)
        @author: dwalker
        :param k: Not to be confused with the key in the calling
                method which was our identifier before the payload.
                This key is now the key in the inner dictionary
                passed through as val from the calling method
        :param v: Not to be confused with the value in the calling
                method which was our inner dictionary passed through
                as val. This val is now the inner dictionary:
                {"val":...,
                 "type": ...,
                 "accept": ...,
                 "result": False}
        :param payloadblock: A list of lines from our payload
                portion of the output from the system_profiler
                command
        :returns: bool - True or False
        '''
        retval = True
        iterator = 0
        for line in payloadblock:
            if re.search("^\"{0,1}" + k + "\"{0,1}=", line):
                if re.search("\{\}$", line):
                    if str(v["val"]) == "{}":
                        return True
                    else:
                        return False
                elif re.search("\{$", line):
                    temp = payloadblock[iterator + 1:]
                    break
            else:
                iterator += 1
        for k2, v2 in v.items():
            if isinstance(v2, list):
                retval = self.checkSimple(k2, v2, temp)
            elif isinstance(v2, tuple):
                retval = self.checkTuple(k2, v2, temp)
            elif isinstance(v2, dict):
                retval = self.checkDict(k2, v2, temp)
            if not retval:
                return False
        return retval

    def setUndoCmd(self, undocmd):
        '''@summary: Mutator method to set self.undocmd to the
                passed in undo command.
        @author: dwalker
        :param undocmd: undo command passed through from
                update method
        '''
        self.undocmd = undocmd

    def setInstallCmd(self, installcmd):
        '''@summary: Mutator method to set self.installcmd to
                thev passed in install command.
        @author: dwalker
        :param installcmd: install command passed through from
                update method
        '''
        self.installcmd = installcmd

    def getUndoCmd(self):
        '''@summary: Accessor method to retrieve self.undocmd
        @author: dwalker
        :returns: self.undocmd
        '''
        return self.undocmd

    def getInstallCmd(self):
        '''@summary: Accessor method to retrieve self.installcmd
        @author: dwalker
        :returns: self.installcmd
        '''
        return self.installcmd

    def update(self):
        '''@summary: Method to set the install command for
        installing the profile for the fix method and set
        the remove command for removing the profile for the
        undo method in upper implementing classes
        @author: dwalker
        :returns: bool - True
        '''
        cmd = ["/usr/bin/profiles", "-I", "-F", self.path]
        self.setInstallCmd(cmd)
        cmd = ["/usr/bin/profiles", "-R", "-F", self.path]
        self.setUndoCmd(cmd)
        return True

    def commit(self):
        '''@summary: Method that performs the install command
                to install the appropriate profile for the
                calling rule.
        @author: dwalker
        :returns: bool - True or False
        '''
        self.ch = CommandHelper(self.logger)
        if self.installcmd:
            if not self.ch.executeCommand(self.installcmd):
                return False
            elif self.ch.getReturnCode() != 0:
                return False
            else:
                return True
        else:
            return False
        return True

    def setbadvalues(self):
        if self.data:
            for k, v in self.data.items():
                for k2, v2, in v.items():
                    if v2["result"] == False:
                        self.badvalues += k2 + " key does not have value of " + str(
                            v2["val"]) + " or doesn't exist\n"
Beispiel #5
0
class networksetup():
    '''This objects encapsulates the complexities of the networksetup command
    on macOS (OS X)
    
    @author: ekkehard j. koch


    '''

###############################################################################

    def __init__(self, logdispatcher):
        self.location = ""
        self.locationIsValidWiFiLocation = False
        self.locationInitialized = False
        self.ns = {}
        self.nsInitialized = False
        self.nso = {}
        self.nsInitialized = False
        self.resultReset()
        self.nsc = "/usr/sbin/networksetup"
        self.logdispatch = logdispatcher

        # This class can, in no way, continue if
        # These constants are undefined, or set to
        # None
        if not DNS:
            self.logdispatch.log(LogPriority.DEBUG, "Please ensure that the following constants, in localize.py, are correctly defined and are not None: DNS, PROXY, PROXYCONFIGURATIONFILE, PROXYDOMAIN. Networksetup.py will not function without these!")
            return None
        elif DNS == None:
            self.logdispatch.log(LogPriority.DEBUG, "Please ensure that the following constants, in localize.py, are correctly defined and are not None: DNS, PROXY, PROXYCONFIGURATIONFILE, PROXYDOMAIN. Networksetup.py will not function without these!")
            return None
        if not PROXY:
            self.logdispatch.log(LogPriority.DEBUG, "Please ensure that the following constants, in localize.py, are correctly defined and are not None: DNS, PROXY, PROXYCONFIGURATIONFILE, PROXYDOMAIN. Networksetup.py will not function without these!")
            return None
        elif PROXY == None:
            self.logdispatch.log(LogPriority.DEBUG, "Please ensure that the following constants, in localize.py, are correctly defined and are not None: DNS, PROXY, PROXYCONFIGURATIONFILE, PROXYDOMAIN. Networksetup.py will not function without these!")
            return None
        if not PROXYCONFIGURATIONFILE:
            self.logdispatch.log(LogPriority.DEBUG, "Please ensure that the following constants, in localize.py, are correctly defined and are not None: DNS, PROXY, PROXYCONFIGURATIONFILE, PROXYDOMAIN. Networksetup.py will not function without these!")
            return None
        elif PROXYCONFIGURATIONFILE == None:
            self.logdispatch.log(LogPriority.DEBUG, "Please ensure that the following constants, in localize.py, are correctly defined and are not None: DNS, PROXY, PROXYCONFIGURATIONFILE, PROXYDOMAIN. Networksetup.py will not function without these!")
            return None
        if not PROXYDOMAIN:
            self.logdispatch.log(LogPriority.DEBUG, "Please ensure that the following constants, in localize.py, are correctly defined and are not None: DNS, PROXY, PROXYCONFIGURATIONFILE, PROXYDOMAIN. Networksetup.py will not function without these!")
            return None
        elif PROXYDOMAIN == None:
            self.logdispatch.log(LogPriority.DEBUG, "Please ensure that the following constants, in localize.py, are correctly defined and are not None: DNS, PROXY, PROXYCONFIGURATIONFILE, PROXYDOMAIN. Networksetup.py will not function without these!")
            return None

        fullproxy = PROXY
        self.ps = fullproxy.split(":")[-2].strip('//')
        self.pp = fullproxy.split(":")[-1]
        self.pf = PROXYCONFIGURATIONFILE
        self.dns = DNS
        self.searchdomain = PROXYDOMAIN
        self.domainByPass = PROXYDOMAINBYPASS
        self.ch = CommandHelper(self.logdispatch)
        self.initialized = False
        self.nameofdevice = ""
        self.notinservicelist = False
        self.detailedresults = ""

###############################################################################

    def report(self):
        '''report is designed to implement the report portion of the stonix rule

        :param self: essential if you override this definition
        @author: ekkehard j. koch
        @change: Breen Malmberg - 12/21/2016 - doc string revision; minor refactor;
                try/except
        :returns: compliant
        :rtype: bool

        '''

        self.logdispatch.log(LogPriority.DEBUG, "Entering networksetup.report()...\n")

        compliant = True
        if not self.initialize():
            self.logdispatch.log(LogPriority.DEBUG, "self.initialize() failed!")
        self.resultReset()

        try:

            if self.locationIsValidWiFiLocation:
                self.resultAppend("WiFi Network Setup for " + \
                                  "services for location named " + \
                                  str(self.location))
            else:
                self.resultAppend("Non-WiFi Network Setup for " + \
                                  "services for location named " + \
                                  str(self.location))

            for key in sorted(self.nso):
                network = self.nso[key]
                networkvalues = self.ns[network]

                networkname = networkvalues["name"]
                networktype = networkvalues["type"]
                networkenabled = networkvalues["enabled"]

                self.logdispatch.log(LogPriority.DEBUG, "key is " + str(key) + "\n")
                self.logdispatch.log(LogPriority.DEBUG, "network name is " + str(networkname) + "\n")
                self.logdispatch.log(LogPriority.DEBUG, "networktype is " + str(networktype) + "\n")
                self.logdispatch.log(LogPriority.DEBUG, "networkenabled is " + str(networkenabled) + "\n")
                self.logdispatch.log(LogPriority.DEBUG, "self.locationIsValidWiFiLocation is " + str(self.locationIsValidWiFiLocation) + "\n")

                if networktype == "bluetooth" and networkenabled:
                    self.logdispatch.log(LogPriority.DEBUG, "networktype is bluetooth and it is enabled. Setting compliant to False!")
                    compliant = False
                    networkvalues["compliant"] = False
                elif networktype == "wi-fi" and networkenabled and not self.locationIsValidWiFiLocation:
                    self.logdispatch.log(LogPriority.DEBUG, "networktype is wi-fi and it is enabled. This is not a valid wi-fi location. Setting compliant to False!")
                    compliant = False
                    networkvalues["compliant"] = False
                else:
                    networkvalues["compliant"] = True

                if networkvalues["compliant"]:
                    messagestring = str(networkname) + " is compliant " + \
                    ": " + str(networkvalues)
                else:
                    messagestring = str(networkname) + " is NOT " + \
                    "compliant : " + str(networkvalues)
                self.resultAppend(str(key) + " - " + messagestring)

            self.logdispatch.log(LogPriority.DEBUG, "Exiting networksetup.report() with compliant = " + str(compliant) + "\n")

        except Exception:
            raise
        return compliant

###############################################################################

    def fix(self):
        '''fix is designed to implement the fix portion of the stonix rule


        :returns: fixed

        :rtype: bool
@author: ekkehard j. koch
@change: Breen Malmberg - 1/12/2017 - added debug logging; doc string edit;
        added try/except

        '''

        self.logdispatch.log(LogPriority.DEBUG, "Entering networksetup.fix()...")

        fixed = True

        self.logdispatch.log(LogPriority.DEBUG, "Running self.initialize()...")
        if not self.initialize():
            self.logdispatch.log(LogPriority.DEBUG, "self.initialize() failed!")
        self.resultReset()

        messagestring = "for location = " + str(self.location)

        try:

            for key in sorted(self.nso):
                network = self.nso[key]
                networkvalues = self.ns[network]
                networkname = networkvalues["name"]
                networktype = networkvalues["type"]
                networkenabled = networkvalues["enabled"]

                self.logdispatch.log(LogPriority.DEBUG, "ns(key, network, networktype, networkenabled) = (" + str(key) + ", " + str(network) + ", " + str(networktype) + ", " + str(networkenabled) + ")")
                self.logdispatch.log(LogPriority.DEBUG, "self.locationIsValidWiFiLocation is " + str(self.locationIsValidWiFiLocation) + "\n")

                if networktype == "bluetooth" and networkenabled:
                    self.logdispatch.log(LogPriority.DEBUG, "Running disableNetworkService(" + str(networkname) + ")...")
                    fixedWorked = self.disableNetworkService(networkname)
                    if fixedWorked:
                        networkvalues["compliant"] = True
                        messagestring = str(networkname) + " fixed " + \
                        ": " + str(networkvalues)
                    else:
                        fixed = False

                elif networktype == "wi-fi" and networkenabled and not self.locationIsValidWiFiLocation:
                    self.logdispatch.log(LogPriority.DEBUG, "Running disableNetworkService(" + str(networkname) + ")...")
                    fixedWorked = self.disableNetworkService(networkname)
                    if fixedWorked:
                        self.logdispatch.log(LogPriority.DEBUG, "Fix worked!")
                        networkvalues["compliant"] = True
                        messagestring = str(networkname) + " fixed " + \
                        ": " + str(networkvalues)
                    else:
                        self.logdispatch.log(LogPriority.DEBUG, "Fix did NOT work!")
                        fixed = False

                elif networktype == "wi-fi" and not networkenabled and self.locationIsValidWiFiLocation:
                    self.logdispatch.log(LogPriority.DEBUG, "Running enableNetwork(" + str(networkname) + ")...")
                    fixedWorked = self.enableNetwork(networkname)
                    if fixedWorked:
                        networkvalues["compliant"] = True
                        messagestring = str(networkname) + " fixed " + \
                        ": " + str(networkvalues)
                    else:
                        fixed = False

                else:
                    networkvalues["compliant"] = True
                    messagestring = ""
                if not messagestring == "":
                    self.resultAppend(messagestring)

        except Exception:
            raise
        return fixed

###############################################################################

    def disableNetworkService(self, pNetworkName):
        '''disable network service
        
        @author: ekkehard j. koch

        :param self: essential if you override this definition
        :param pNetworkName: name of network
        :returns: boolean - true
        @note: None
        @change: Breen Malmberg - 3/23/2016 - wifi will now be disabled via
                setairportpower if not found in the service list.
        @change: Breen Malmberg - 12/20/2016 - minor refactor; parameter validation
                ;logging
        @change: Breen Malmberg - 1/12/2017 - added more debug logging

        '''

        self.logdispatch.log(LogPriority.DEBUG, "Entering networksetup.disableNetworkService()...")

        success = True
        networkName = pNetworkName.strip()

        try:

            if not isinstance(pNetworkName, str):
                self.logdispatch.log(LogPriority.DEBUG, "Specified parameter: pNetworkName must be of type: string. Got: " + str(type(pNetworkName)))
                success = False

            if not pNetworkName:
                self.logdispatch.log(LogPriority.DEBUG, "Specified parameter: pNetworkName is blank or None!")
                success = False

            self.logdispatch.log(LogPriority.DEBUG, "\nnetworkName = " + str(networkName).strip().lower() + "\n")
            self.logdispatch.log(LogPriority.DEBUG, "\nself.nameofdevice = " + str(self.nameofdevice).strip().lower() + "\n")

            if str(networkName).strip().lower() == str(self.nameofdevice).strip().lower():

                self.logdispatch.log(LogPriority.DEBUG, "networkName matches self.nameofdevice. Running airportpower disable command...")

                disablecommand = [self.nsc, "-setairportpower", networkName, "off"]

                self.ch.executeCommand(disablecommand)
                
                if self.ch.getReturnCode() != 0:
                    success = False
                    self.logdispatch.log(LogPriority.DEBUG, "Execution of command failed: " + str(disablecommand))
                else:
                    self.logdispatch.log(LogPriority.DEBUG, "Command executed successfully: " + str(disablecommand))
            else:
                if success:
                    command = [self.nsc, "-setnetworkserviceenabled", networkName, "off"]
                    self.ch.executeCommand(command)
                    if self.ch.getReturnCode() != 0:
                        success = False
                        self.logdispatch.log(LogPriority.DEBUG, "Execution of command failed: " + str(command))

            if not success:
                self.logdispatch.log(LogPriority.DEBUG, "networksetup.disableNetworkService() Failed")
            else:
                self.logdispatch.log(LogPriority.DEBUG, "networksetup.disableNetworkService() was Successful")

            self.logdispatch.log(LogPriority.DEBUG, "Exiting networksetup.disableNetworkService()")

        except Exception:
            success = False
            raise
        return success

###############################################################################

    def enableNetwork(self, pNetworkName):
        '''enable network service
        
        @author: ekkehard j. koch

        :param self: essential if you override this definition
        :param pNetworkName: name of network
        :returns: boolean - true
        @note: None
        @change: Breen Malmberg - 3/23/2016 - wifi will now be enabled via
            setairportpower if not found in the service list.
        @change: Breen Malmberg - 12/20/2016 - minor refactor; parameter validation
                ;logging

        '''

        self.logdispatch.log(LogPriority.DEBUG, "Entering networksetup.enableNetwork()...")

        success = True
        networkName = pNetworkName.strip()

        try:

            if not isinstance(pNetworkName, str):
                self.logdispatch.log(LogPriority.DEBUG, "Specified parameter: pNetworkName must be of type: string. Got: " + str(type(pNetworkName)))
                success = False

            if not pNetworkName:
                self.logdispatch.log(LogPriority.DEBUG, "Specified parameter: pNetworkName is blank or None!")
                success = False

            if str(networkName).strip().lower() == str(self.nameofdevice).strip().lower() and self.notinservicelist:
                enablecommand = [self.nsc, "-setairportpower", networkName, "on"]
                self.ch.executeCommand(enablecommand)
                if self.ch.getReturnCode() != 0:
                    success = False
                    self.logdispatch.log(LogPriority.DEBUG, "Execution of command failed: " + str(enablecommand))
            else:
                if networkName == "":
                    success = False
                if success:
                    command = [self.nsc, "-setnetworkserviceenabled", networkName, "on"]
                    self.ch.executeCommand(command)
                    if self.ch.getReturnCode() != 0:
                        success = False
                        self.logdispatch.log(LogPriority.DEBUG, "Execution of command failed: " + str(command))

            if not success:
                self.logdispatch.log(LogPriority.DEBUG, "networksetup.enableNetwork() Failed")
            else:
                self.logdispatch.log(LogPriority.DEBUG, "networksetup.enableNetwork() was Successful")

            self.logdispatch.log(LogPriority.DEBUG, "Exiting networksetup.enableNetwork()")

        except (KeyboardInterrupt, SystemExit):
# User initiated exit
            raise
        except Exception:
            raise
        return success

###############################################################################

    def getDetailedresults(self):
        '''get the detailed results text
        
        @author: ekkehard j. koch

        :param self: essential if you override this definition
        :param pLocationName: location name
        :returns: string: detailedresults
        @note: None

        '''
        return self.detailedresults

###############################################################################

    def getLocation(self):
        '''get the location used by on the mac
        
        @author: ekkehard j. koch

        :param self: essential if you override this definition
        :returns: boolean - true
        @note: None

        '''
        try:
            success = True
            command = [self.nsc, "-getcurrentlocation"]
            self.ch.executeCommand(command)
            for line in self.ch.getOutput():
                lineprocessed = line.strip()
                self.location = lineprocessed
                self.locationInitialized = True
            self.locationIsValidWiFiLocation = self.isValidLocationName(self.location)

            self.logdispatch.log(LogPriority.DEBUG, "Is this a valid WiFi location? " + str(self.locationIsValidWiFiLocation))

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            success = False
            raise
        return success

###############################################################################

    def initialize(self):
        '''initialize the object
        
        @author: ekkehard j. koch


        :returns: self.initalized

        :rtype: bool
@change: Breen Malmberg - 1/12/2017 doc string fix; default init self.initialized to False;
        added try/except

        '''

        self.initialized = False

        try:

            if not self.initialized:
                self.getLocation()
                self.updateCurrentNetworkConfigurationDictionary()
                self.initialized = True

        except Exception:
            raise
        return self.initialized

###############################################################################

    def isValidLocationName(self, pLocationName=""):
        '''determine if this is a valid wifi location
        
        @author: ekkehard j. koch

        :param self: essential if you override this definition
        :param pLocationName: location name (Default value = "")
        :returns: boolean - true
        @note: None

        '''
        success = False
        pLocationName = pLocationName.strip()
        if pLocationName == "" or re.match("^\s+$", pLocationName):
            locationName = self.location.lower().strip()
        else:
            locationName = pLocationName.lower()
        if 'wi-fi' in locationName:
            success = True
        elif 'wifi' in locationName:
            success = True
        elif 'wireless' in locationName:
            success = True
        elif 'airport' in locationName:
            success = True
        elif 'off-site' in locationName:
            success = True
        elif 'offsite' in locationName:
            success = True
        else:
            success = False
        return success

###############################################################################

    def networksetupistnetworkserviceorderoutputprocessing(self, outputLines):

        success = True
        order = -1
        networkenabled = False
        newserviceonnexline = False
        newservice = False
        servicename = ""
        networktype = False
        for line in outputLines:
            lineprocessed = line.strip()
            if newserviceonnexline:
                newservice = True
                newserviceonnexline = False
            else:
                newservice = False
                newserviceonnexline = False
            if lineprocessed == "An asterisk (*) denotes that a network service is disabled.":
                infoOnThisLine = False
                newserviceonnexline = True
            elif lineprocessed == "":
                infoOnThisLine = False
                newserviceonnexline = True
            else:
                infoOnThisLine = True
            if newservice and infoOnThisLine:
                self.logdispatch.log(LogPriority.DEBUG, "New service and info line: " + str(line))
                order = order + 1
# see if network is enabled
                if lineprocessed[:3] == "(*)":
                    networkenabled = False
                else:
                    networkenabled = True
                linearray = lineprocessed.split()
                linearray = linearray[1:]
                servicename = ""
                for item in linearray:
                    if servicename == "":
                        servicename = item
                    else:
                        servicename = servicename + " " + item

                if "ethernet" in servicename.lower():
                    networktype = "ethernet"
                elif "lan" in servicename.lower():
                    #####
                    # The belkin dongles LANL has chosen to use for Apple
                    # laptops does not identify itself vi convention,
                    # so this is the choice roy is making to indicate the
                    # mapping between "Belkin USB-C LAN" and ethernet.
                    networktype = "ethernet"
                elif "bluetooth" in servicename.lower():
                    networktype = "bluetooth"
                elif "usb" in servicename.lower():
                    networktype = "usb"
                elif "wi-fi" in item.lower():
                    networktype = "wi-fi"
                elif "firewire" in servicename.lower():
                    networktype = "firewire"
                elif "thunderbolt" in servicename.lower():
                    networktype = "thunderbolt"
                else:
                    networktype = "unknown"
                self.ns[servicename] = {"name": servicename.strip(),
                                        "hardware port":  servicename.strip(),
                                        "enabled": networkenabled,
                                        "type": networktype.strip()}
# determine network type
            elif infoOnThisLine:
                self.logdispatch.log(LogPriority.DEBUG, "Info line: " + str(line))
                lineprocessed = lineprocessed.strip("(")
                lineprocessed = lineprocessed.strip(")")
                linearray = lineprocessed.split(",")
                for item in linearray:
                    lineprocessed = item.strip()
                    itemarray = lineprocessed.split(":")
                    if servicename != "":
                        if len(itemarray) > 1:
                            self.ns[servicename][itemarray[0].strip().lower()] = itemarray[1].strip()
# update dictionary entry for network
                    self.logdispatch.log(LogPriority.DEBUG, "(servicename, enabled, networktype): (" + \
                                         str(servicename).strip() + ", " + str(networkenabled) + ", " + \
                                         str(networktype).strip() + ")")
# create an ordered list to look up later
                    orderkey = str(order).zfill(4)
                    self.nso[orderkey] = servicename.strip()
                    self.updateNetworkConfigurationDictionaryEntry(servicename.strip())
        self.setNetworkServiceOrder()
        return success

###############################################################################

    def networksetuplistallhardwareportsoutputprocessing(self, outputLines):

        success = True
        newserviceonnexline = False
        newservice = False
        servicename = ""
        # noinfo = False
        for line in outputLines:
            lineprocessed = line.strip()
            if newserviceonnexline:
                newservice = True
                newserviceonnexline = False
            else:
                newservice = False
                newserviceonnexline = False
            if lineprocessed == "":
                infoOnThisLine = False
                newserviceonnexline = True
            else:
                infoOnThisLine = True
# Get info from first new service line
            if newserviceonnexline and not servicename == "":
                self.updateNetworkConfigurationDictionaryEntry(servicename)
            elif lineprocessed == "VLAN Configurations":
                break
            elif newservice and infoOnThisLine:
                self.logdispatch.log(LogPriority.DEBUG, "New service and info line: " + str(line))
                linearray = lineprocessed.split(":")
                linearray = linearray[1:]
                servicename = ""
                for item in linearray:
                    if servicename == "":
                        servicename = item.strip()
                    else:
                        servicename = servicename + " " + item.strip()
                if "ethernet" in servicename.lower():
                    networktype = "ethernet"
                elif "lan" in servicename.lower():
                    #####
                    # The belkin dongles LANL has chosen to use for Apple
                    # laptops does not identify itself vi convention,
                    # so this is the choice roy is making to indicate the
                    # mapping between "Belkin USB-C LAN" and ethernet.
                    networktype = "ethernet"
                elif "bluetooth" in servicename.lower():
                    networktype = "bluetooth"
                elif "usb" in servicename.lower():
                    networktype = "usb"
                elif "wi-fi" in servicename.lower():
                    networktype = "wi-fi"
                elif "firewire" in servicename.lower():
                    networktype = "firewire"
                elif "thunderbolt" in servicename.lower():
                    networktype = "thunderbolt"
                else:
                    networktype = "unknown"
                self.ns[servicename] = {"name": servicename.strip(),
                                        "hardware port": servicename.strip(),
                                        "type": networktype.strip()}
# determine network type
            elif infoOnThisLine:
                self.logdispatch.log(LogPriority.DEBUG, "Info line: " + str(line))
                linearray = lineprocessed.split()
                colonFound = False
                nameOfItem = ""
                valueOfItem = ""
                for item in linearray:
                    processedItem = item.strip()
                    if not colonFound:
                        if ":" in item:
                            colonFound = True
                            processedItem = item.strip(":")
                        if nameOfItem == "":
                            nameOfItem = processedItem.lower()
                        else:
                            nameOfItem = nameOfItem + " " + processedItem.lower()
                    else:
                        if valueOfItem == "":
                            valueOfItem = processedItem
                        else:
                            valueOfItem = valueOfItem + " " + processedItem
                if not valueOfItem == "" and not nameOfItem == "":
                    self.ns[servicename][nameOfItem] = valueOfItem.strip()
        return success

###############################################################################

    def resultAppend(self, pMessage=""):
        '''reset the current kveditor values to their defaults.
        @author: ekkehard j. koch

        :param self: essential if you override this definition
        :param pMessage: message to be appended (Default value = "")
        :returns: boolean - true
        @note: None

        '''
        datatype = type(pMessage)
        if datatype == str:
            if not (pMessage == ""):
                messagestring = pMessage
                if (self.detailedresults == ""):
                    self.detailedresults = messagestring
                else:
                    self.detailedresults = self.detailedresults + "\n" + \
                                           messagestring
        elif datatype == list:
            if not (pMessage == []):
                for item in pMessage:
                    messagestring = item
                    if (self.detailedresults == ""):
                        self.detailedresults = messagestring
                    else:
                        self.detailedresults = self.detailedresults + "\n" + \
                        messagestring
        else:
            raise TypeError("pMessage with value" + str(pMessage) + \
                            "is of type " + str(datatype) + " not of " + \
                            "type " + str(str) + \
                            " or type " + str(list) + \
                            " as expected!")

###############################################################################

    def resultReset(self):
        '''reset the current kveditor values to their defaults.
        @author: ekkehard j. koch

        :param self: essential if you override this definition
        :returns: boolean - true
        @note: kveditorName is essential

        '''
        self.detailedresults = ""

###############################################################################

    def setAdvancedNetworkSetup(self, pHardwarePort = None):
        '''Set proxies up for normal first configuration that has a network
        connection.
        
        @author: Roy Nielsen

        :param self: essential if you override this definition
        :param pNetworkName: name of the network to fix
        :param pHardwarePort:  (Default value = None)
        :returns: boolean - true
        @note: None

        '''
        success = True
        if pHardwarePort == None:
            self.initialize()
            self.setNetworkServiceOrder()
            for key in sorted(self.nso):
                network = self.nso[key]
                networkvalues = self.ns[network]
                networkname = networkvalues["name"]
                networktype = networkvalues["type"]
                networkhardwarePort = networkvalues["hardware port"]
                networkenabled = networkvalues["enabled"]
                msg = "networkname " + str(networkname) + "; networktype " + str(networktype) + \
                "; networkhardwarePort " + str(networkhardwarePort) + "; networkenabled " + \
                str(networkenabled)
                self.logdispatch.log(LogPriority.DEBUG, msg)
                if networkenabled and (networktype == "wifi" or networktype == "ethernet"):
                    msg = "Enabled Network Found; " + msg
                    self.logdispatch.log(LogPriority.DEBUG, msg)
                    break
        else:
            networkhardwarePort = pHardwarePort.strip()
            networkenabled = True
# Set the DNS servers
        if not networkhardwarePort == "" and networkenabled:
            command = self.nsc + " -setdnsservers '" + str(networkhardwarePort) + "' " + self.dns
            self.ch.executeCommand(command)
            if self.ch.getError():
                msg = command + " output: " + str(self.ch.getOutput())
                self.logdispatch.log(LogPriority.DEBUG, msg)
                success = False
# Set the Search Domain
            command = self.nsc + " -setsearchdomains '" + str(networkhardwarePort) + "' " + self.searchdomain
            self.ch.executeCommand(command)
            if self.ch.getError():
                msg = command + " output: " + str(self.ch.getOutput())
                self.logdispatch.log(LogPriority.DEBUG, msg)
                success = False
# set up the auto proxy URL
            command = self.nsc + " -setautoproxyurl '" + str(networkhardwarePort) + "' " + self.pf
            self.ch.executeCommand(command)
            if self.ch.getError():
                msg = command + " output: " + str(self.ch.getOutput())
                self.logdispatch.log(LogPriority.DEBUG, msg)
                success = False
# Set up the FTP proxy
            command = self.nsc + " -setftpproxy '" + str(networkhardwarePort) + "' " + self.ps + " " + self.pp
            self.ch.executeCommand(command)
            if self.ch.getError():
                msg = command + " output: " + str(self.ch.getOutput())
                self.logdispatch.log(LogPriority.DEBUG, msg)
                success = False
# Set up the HTTPS proxy
            command = self.nsc + " -setsecurewebproxy '" + str(networkhardwarePort) + "' " + self.ps + " " + self.pp
            self.ch.executeCommand(command)
            if self.ch.getError():
                msg = command + " output: " + str(self.ch.getOutput())
                self.logdispatch.log(LogPriority.DEBUG, msg)
                success = False
# Set up the web proxy
            command = self.nsc + " -setwebproxy '" + str(networkhardwarePort) + "' " + self.ps + " " + self.pp
            self.ch.executeCommand(command)
            if self.ch.getError():
                msg = command + " output: " + str(self.ch.getOutput())
                self.logdispatch.log(LogPriority.DEBUG, msg)
                success = False
# Get current proxy bypass domains and add self.searchdomain
            command = self.nsc + " -getproxybypassdomains '" + str(networkhardwarePort) + "' "
            self.ch.executeCommand(command)
            if not self.ch.getError():
                command = self.nsc + " -setproxybypassdomains '" + str(networkhardwarePort) + "'"
                for item in self.ch.getOutput() :
                    if not re.match("^\s*$", item) :
                        command = command + " " + str(item.strip())
                if not self.domainByPass in command:
                    command = command + " " + str(self.domainByPass)
                    self.ch.executeCommand(command)
                    if not self.ch.getError():
                        success = False
            else:
                msg = command + " output: " + str(self.ch.getOutput())
                self.logdispatch.log(LogPriority.DEBUG, msg)
                success = False
        return success

###############################################################################

    def setNetworkServiceOrder(self):
        ''' '''
        #####
        # Find the interface that needs to be at the top of the self.nso order
        cmd = ["/sbin/route", "get", "default"]

        self.ch.executeCommand(cmd)
        defaultInterface = None

        for line in self.ch.getOutput():
            try:
                interface_match = re.match("\s+interface:\s+(\w+)", line)
                defaultInterface = interface_match.group(1)
            except (IndexError, KeyError, AttributeError) as err:
                self.logdispatch.log(LogPriority.DEBUG, str(line) + " : " + str(err))
            else:
                self.logdispatch.log(LogPriority.DEBUG, "Found: " + str(line))
                break

        #####
        # Find the interface/service name via networksetup -listallhardwareports
        cmd = ["/usr/sbin/networksetup", "-listallhardwareports"]

        self.ch.executeCommand(cmd)

        hardwarePort = ""
        device = ""
        enet = ""

        for line in self.ch.getOutput():
            try:
                hw_match = re.match("^Hardware Port:\s+(.*)\s*$", line)
                hardwarePort = hw_match.group(1)
                #print hardwarePort
            except AttributeError as err:
                pass
            try:
                #print line
                dev_match = re.match("^Device:\s+(.*)\s*$", line)
                device = dev_match.group(1)
                #print str(device)
            except AttributeError as err:
                pass
            try:
                enet_match = re.match("^Ethernet Address:\s+(\w+:\w+:\w+:\w+:\w+:\w+)\s*$", line)
                enet = enet_match.group(1)
                self.logger.log(LogPriority.DEBUG, "enet: " + str(enet))
            except AttributeError as err:
                pass

            if re.match("^$", line) or re.match("^\s+$", line):
                if re.match("^%s$"%str(device), str(defaultInterface)):
                    self.logdispatch.log(LogPriority.DEBUG, device)
                    self.logdispatch.log(LogPriority.DEBUG,  defaultInterface)
                    break
                hardwarePort = ""
                device = ""
                enet = ""

        #####
        # Reset NSO order if the defaultInterface is not at the top of the list
        newnso = {}
        i = 1

        self.logdispatch.log(LogPriority.DEBUG, str(self.nso))
        self.logdispatch.log(LogPriority.DEBUG, "hardware port: " + hardwarePort)

        for key, value in sorted(self.nso.items()):
            #print str(key) + " : " + str(value)
            if re.match("^%s$"%hardwarePort.strip(), value.strip()):
                key = re.sub("^\d\d\d\d$", "0000", key)
                newnso[key] = value
            else:
                orderkey = str(i).zfill(4)
                newnso[orderkey] = value
                i = i + 1
                self.logdispatch.log(LogPriority.DEBUG, str(newnso))
        #print str(newnso)
        self.nso = newnso
        self.logdispatch.log(LogPriority.DEBUG, str(self.nso))
        for key, value in sorted(self.nso.items()):
            self.logdispatch.log(LogPriority.DEBUG, str(key) + " : " + str(value))

        for item in self.ns: 
            if re.match("^%s$"%hardwarePort.strip(), self.ns[item]["name"]) and self.ns[item]["type"] is "unknown" and re.match("^en", defaultInterface):
                self.ns[item]["type"] = "ethernet"

###############################################################################

    def startup(self):
        '''startup is designed to implement the startup portion of the stonix rule
        
        @author: ekkehard j. koch


        '''
        disabled = True
        self.initialize()
        messagestring = "for location = " + str(self.location)
        for key in sorted(self.nso):
            network = self.nso[key]
            networkvalues = self.ns[network]
            networkname = networkvalues["name"]
            networktype = networkvalues["type"]
            networkenabled = networkvalues["enabled"]
            if networktype == "bluetooth" and networkenabled:
                fixedWorked = self.disableNetworkService(networkname)
                if fixedWorked:
                    networkvalues["compliant"] = True
                    messagestring = str(networkname) + " fixed " + \
                    ": " + str(networkvalues)
                else:
                    disabled = False
            elif networktype == "wi-fi" and networkenabled:
                fixedWorked = self.disableNetworkService(networkname)
                if fixedWorked:
                    networkvalues["compliant"] = True
                    messagestring = str(networkname) + " fixed " + \
                    ": " + str(networkvalues)
                else:
                    disabled = False
            else:
                networkvalues["compliant"] = True
                messagestring = ""
            if not messagestring == "":
                self.resultAppend(messagestring)
        return disabled

###############################################################################

    def updateCurrentNetworkConfigurationDictionary(self):
        '''update the network configuration dictianry
        
        @author: ekkehard j. koch

        :param self: essential if you override this definition
        :returns: boolean - true
        @note: None
        @change: Breen Malmberg - 3/23/2016 - added code to find and disable
                wi-fi on el capitan, via hardware ports instead of just service

        '''

        self.logdispatch.log(LogPriority.DEBUG, "Entering updateCurrentNetworkConfigurationDictionary()...")

        try:

            success = True

# issue networksetup -listallhardwareports to get all network services
            if success:
                command = [self.nsc, "-listallhardwareports"]
                self.ch.executeCommand(command)
                self.logdispatch.log(LogPriority.DEBUG, "Building ns dictionary from command: " + str(command))
                success = self.networksetuplistallhardwareportsoutputprocessing(self.ch.getOutput())

# issue networksetup -listallnetworkservices to get all network services
            if success:
                command = [self.nsc, "-listnetworkserviceorder"]
                self.ch.executeCommand(command)
                self.logdispatch.log(LogPriority.DEBUG, "Building ns dictionary from command: " + str(command))
                success = self.networksetupistnetworkserviceorderoutputprocessing(self.ch.getOutput())

# set ns init and nso init status
            self.nsInitialized = True
            self.nsoInitialized = True

            self.logdispatch.log(LogPriority.DEBUG, "Exiting updateCurrentNetworkConfigurationDictionary()...")

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            success = False
            raise
        return success

###############################################################################

    def updateNetworkConfigurationDictionaryEntry(self, pKey):
        '''update a single network configuration dictionary entry
        
        @author: ekkehard j. koch

        :param self: essential if you override this definition
        :param pkey: key for the dictinary entry
        :param pKey: 
        :returns: boolean - true
        @note: None
        @change: Breen Malmberg - 1/12/2017 - doc string edit; added debug logging;
                default var init success to True; added code to update the Wi-Fi entry;
        @change: Roy Nielsen - 3/6/2018 - Changed algo to look at
                                          'Device' rather than 'name'
                                          when getting the airport power
                                          status

        '''
        pKey = pKey.strip()
        self.logdispatch.log(LogPriority.DEBUG, "Entering networksetup.updateNetworkConfigurationDictionaryEntry() with pKey=" + str(pKey) + "...")

        success = True
        key = pKey

        try:
            success = True
            key = pKey
            entry = self.ns[key]

            if success:
                if entry == None:
                    success = False
                    self.logdispatch.log(LogPriority.DEBUG, "self.ns[" + str(key) + "] was not found! success set to False.")

            if success:
                command = [self.nsc, "-getmacaddress", key]
                self.ch.executeCommand(command)
                for line in self.ch.getOutput():
                    try:
                        macaddress = re.search("(\w\w:\w\w:\w\w:\w\w:\w\w:\w\w)",
                                               line.strip()).group(1)
                    except:
                        macaddress = ""
                    self.ns[key]["macaddress"] = macaddress

            if success:
                # added for disabling by device name 1/11/2017
                if key == "Wi-Fi":
                    self.logdispatch.log(LogPriority.DEBUG, "Updating Wi-Fi device entry for: " + str(self.ns[key]["name"]))
                    command = [self.nsc, "-getairportpower", self.ns[key]["Device"]]
                    self.ch.executeCommand(command)
                    for line in self.ch.getOutput():
                        if re.search("Wi-Fi\s+Power.*On", line, re.IGNORECASE):
                            self.ns[key]["enabled"] = True
                            self.logdispatch.log(LogPriority.DEBUG, "airportpower for device " + str(self.ns[key]["name"]) + " is: On")
                        else:
                            self.ns[key]["enabled"] = False
                            self.logdispatch.log(LogPriority.DEBUG, "airportpower for device " + str(self.ns[key]["name"]) + " is: Off")
                else:
                    # original code (only for services)
                    command = [self.nsc,
                           "-getnetworkserviceenabled", key]
                    self.ch.executeCommand(command)
                    for line in self.ch.getOutput():
                        lineprocessed = line.strip()
                        if lineprocessed == "Enabled":
                            self.ns[key]["enabled"] = True
                        else:
                            self.ns[key]["enabled"] = False

            self.logdispatch.log(LogPriority.DEBUG, "Exiting networksetup.updateNetworkConfigurationDictionaryEntry() and returning success=" + str(success))
        except KeyError:
            self.logdispatch.log(LogPriority.DEBUG, "Key error...")
        except (KeyboardInterrupt, SystemExit):
            # User initiated exit
            raise
        except Exception:
            success = False
            raise

        return success
Beispiel #6
0
class Zypper(object):
    """The template class that provides a framework that must be implemented by
    all platform specific pkgmgr classes.
    
    @author: Derek T Walker
    @change: 2012/08/08 Derek Walker - Original Implementation
    @change: 2014/09/10 dkennel - Added -n option to search command string
    @change: 2014/12/24 Breen Malmberg - fixed a typo in the old search string;
            fixed multiple pep8 violations; changed search strings to be match exact and
            search for installed or available separately
    @change: 2015/08/20 eball - Added getPackageFromFile and self.rpm var
    @change: 2016/08/02 eball - Moved checkInstall return out of else block
    @change: 2017/04/19 Breen Malmberg - refactored multiple methods; cleaned up doc
            strings; added logging; added two methods: Update and checkUpdate;
            removed detailedresults reset in __init__ (this should always be handled
            in the calling rule); replaced detailedresults instances with logging;
            added the flag "--quiet" to the install variable


    """
    def __init__(self, logger):
        self.logger = logger
        self.ch = CommandHelper(self.logger)
        self.zyploc = "/usr/bin/zypper"
        self.install = self.zyploc + " --non-interactive --quiet install "
        self.remove = self.zyploc + " --non-interactive remove "
        self.searchi = self.zyploc + " --non-interactive search --match-exact -i "
        self.searchu = self.zyploc + " --non-interactive search --match-exact -u "
        self.updates = self.zyploc + " lu "
        self.upzypp = self.zyploc + " up "
        self.rpm = "/usr/bin/rpm -q "
        self.pkgtype = "zypper"
        self.pkgerrs = [1, 2, 3, 4, 5, 6]
        self.pkgnotfound = [104]

    def installpackage(self, package):
        """Install a package. Return a bool indicating success or failure.

        :param package: string; Name of the package to be installed, must be
                recognizable to the underlying package manager.
        :returns: installed
        :rtype: bool
@author: Derek Walker
@change: Breen Malmberg - 12/24/2014 - fixed method doc string formatting
@change: Breen Malmberg - 10/1/2018 - added check for package manager lock and retry loop

        """

        installed = True
        maxtries = 12
        trynum = 0

        while psRunning("zypper"):
            trynum += 1
            if trynum == maxtries:
                self.logger.log(
                    LogPriority.DEBUG,
                    "Timed out while attempting to install package due to zypper package manager being in-use by another process."
                )
                installed = False
                return installed
            else:
                self.logger.log(
                    LogPriority.DEBUG,
                    "zypper package manager is in-use by another process. Waiting for it to be freed..."
                )
                time.sleep(5)

        try:

            self.ch.executeCommand(self.install + package)
            retcode = self.ch.getReturnCode()
            if retcode in self.pkgerrs:
                errstr = self.ch.getErrorString()
                self.logger.log(LogPriority.DEBUG,
                                "Package installation because:\n" + errstr)
                installed = False
            elif retcode in self.pkgnotfound:
                self.logger.log(
                    LogPriority.DEBUG,
                    "Package installation failed because zypper could not find a package named: "
                    + str(package))
                installed = False

            if installed:
                self.logger.log(
                    LogPriority.DEBUG,
                    "Package " + str(package) + " installed successfully")
            else:
                self.logger.log(LogPriority.DEBUG,
                                "Failed to install package " + str(package))

        except Exception:
            raise
        return installed

    def removepackage(self, package):
        """Remove a package. Return a bool indicating success or failure.

        :param package: string; Name of the package to be removed, must be
                recognizable to the underlying package manager.
        :returns: removed
        :rtype: bool
@author: Derek Walker
@change: 12/24/2014 - Breen Malmberg - fixed method doc string formatting;
        fixed an issue with var 'removed' not
        being initialized before it was called

        """

        removed = True
        maxtries = 12
        trynum = 0

        while psRunning("zypper"):
            trynum += 1
            if trynum == maxtries:
                self.logger.log(
                    LogPriority.DEBUG,
                    "Timed out while attempting to remove package due to zypper package manager being in-use by another process."
                )
                removed = False
                return removed
            else:
                self.logger.log(
                    LogPriority.DEBUG,
                    "zypper package manager is in-use by another process. Waiting for it to be freed..."
                )
                time.sleep(5)

        try:

            self.ch.executeCommand(self.remove + package)
            retcode = self.ch.getReturnCode()
            if retcode in self.pkgerrs:
                errstr = self.ch.getErrorString()
                self.logger.log(LogPriority.DEBUG,
                                "Package removal failed because:\n" + errstr)
                removed = False
            elif retcode in self.pkgnotfound:
                self.logger.log(
                    LogPriority.DEBUG, "No package found matching: " +
                    str(package) + ". Nothing to remove")

            if removed:
                self.logger.log(
                    LogPriority.DEBUG,
                    "Package " + str(package) + " was removed successfully")
            else:
                self.logger.log(LogPriority.DEBUG,
                                "Failed to remove package " + str(package))

        except Exception:
            raise
        return removed

    def checkInstall(self, package):
        """Check the installation status of a package. Return a bool; True if
        the package is installed.

        :param string package: Name of the package whose installation status
            is to be checked, must be recognizable to the underlying package
            manager.
        :param package: 
        :returns: bool
        @author: Derek Walker
        @change: 12/24/2014 - Breen Malmberg - fixed method doc string formatting
        @change: 12/24/2014 - Breen Malmberg - changed var name 'found' to
            'installed'
        @change: 12/24/2014 - Breen Malmberg - now uses correct search syntax
        @change: 12/24/2014 - Breen Malmberg - removed detailedresults update on
            'found but not installed' as this no longer applies to this method

        """

        installed = True
        maxtries = 12
        trynum = 0

        while psRunning("zypper"):
            trynum += 1
            if trynum == maxtries:
                self.logger.log(
                    LogPriority.DEBUG,
                    "Timed out while attempting to check status of  package due to zypper package manager being in-use by another process."
                )
                installed = False
                return installed
            else:
                self.logger.log(
                    LogPriority.DEBUG,
                    "zypper package manager is in-use by another process. Waiting for it to be freed..."
                )
                time.sleep(5)

        try:

            self.ch.executeCommand(self.searchi + package)
            retcode = self.ch.getReturnCode()
            if retcode in self.pkgerrs:
                errstr = self.ch.getErrorString()
                self.logger.log(
                    LogPriority.DEBUG, "Failed to check for package: " +
                    str(package) + " because:\n" + errstr)
                installed = False
            elif retcode in self.pkgnotfound:
                installed = False

            if installed:
                self.logger.log(LogPriority.DEBUG,
                                " Package " + str(package) + " is installed")
            else:
                self.logger.log(
                    LogPriority.DEBUG,
                    " Package " + str(package) + " is NOT installed")

        except Exception:
            raise
        return installed

    def checkAvailable(self, package):
        """check if given package is available to install on the current system

        :param package: 
        :returns: bool
        @author: Derek Walker
        @change: 12/24/2014 - Breen Malmberg - added method documentation
        @change: 12/24/2014 - Breen Malmberg - changed var name 'found' to
            'available'
        @change: 12/24/2014 - Breen Malmberg - fixed search syntax and updated search
            variable name
        @change: Breen Malmberg - 5/1/2017 - replaced detailedresults with logging;
                added parameter validation

        """

        available = True
        found = False
        maxtries = 12
        trynum = 0

        while psRunning("zypper"):
            trynum += 1
            if trynum == maxtries:
                self.logger.log(
                    LogPriority.DEBUG,
                    "Timed out while attempting to check availability of package, due to zypper package manager being in-use by another process."
                )
                available = False
                return available
            else:
                self.logger.log(
                    LogPriority.DEBUG,
                    "zypper package manager is in-use by another process. Waiting for it to be freed..."
                )
                time.sleep(5)

        try:

            self.ch.executeCommand(self.searchu + package)
            retcode = self.ch.getReturnCode()
            if retcode in self.pkgerrs:
                errstr = self.ch.getErrorString()
                self.logger.log(
                    LogPriority.DEBUG, "Failed to check if package: " +
                    str(package) + " is available, because:\n" + errstr)
                available = False
            elif retcode in self.pkgnotfound:
                available = False
            else:
                output = self.ch.getOutput()
                for line in output:
                    if re.search(package, line):
                        found = True
                if not found:
                    available = False

            if available:
                self.logger.log(
                    LogPriority.DEBUG,
                    "Package " + str(package) + " is available to install")
            else:
                self.logger.log(
                    LogPriority.DEBUG,
                    "Package " + str(package) + " is NOT available to install")

        except Exception:
            raise
        return available

    def checkUpdate(self, package=""):
        """check for available updates for specified
        package.
        if no package is specified, then check for
        updates to the entire system.

        :param package: string; name of package to check (Default value = "")
        :returns: updatesavail
        :rtype: bool
@author: Breen Malmberg

        """

        # zypper does not have a package-specific list updates mechanism
        # you have to list all updates or nothing

        updatesavail = True

        try:

            if package:
                self.ch.executeCommand(self.updates + " | grep " + package)
            else:
                self.ch.executeCommand(self.updates)

            retcode = self.ch.getReturnCode()
            if retcode in [2, 3, 4, 5, 6]:
                errstr = self.ch.getErrorString()
                self.logger.log(
                    LogPriority.DEBUG,
                    "Failed to check for updates because:\n" + errstr)
                updatesavail = False
            elif retcode in self.pkgnotfound:
                updatesavail = False

            if not updatesavail:
                self.logger.log(LogPriority.DEBUG, "No updates available")
            else:
                self.logger.log(LogPriority.DEBUG, "Updates available")

        except Exception:
            raise
        return updatesavail

    def Update(self, package=""):
        """update a specified package
        if no package name is specified,
        then update all packages on the system

        :param package: string; name of package to update (Default value = "")
        :returns: updated
        :rtype: bool
@author: Breen Malmberg

        """

        updated = True

        try:

            self.ch.executeCommand(self.upzypp + package)
            retcode = self.ch.getReturnCode()
            if retcode in self.pkgerrs:
                errstr = self.ch.getErrorString()
                self.logger.log(LogPriority.DEBUG,
                                "Failed to update because:\n" + errstr)
                updated = False
            elif retcode in self.pkgnotfound:
                self.logger.log(
                    LogPriority.DEBUG,
                    "Unable to find package named: " + str(package))
                updated = False

            if updated:
                self.logger.log(LogPriority.DEBUG,
                                "Updates applied successfully")
            else:
                self.logger.log(LogPriority.DEBUG, "Failed to apply updates")

        except Exception:
            raise
        return updated

    def getPackageFromFile(self, filename):
        """Returns the name of the package that provides the given
        filename/path.

        :param filename: 
        :returns: string name of package if found, None otherwise
        @author: Eric Ball

        """

        packagename = ""

        try:

            self.ch.executeCommand(self.rpm + "-f " + filename)
            retcode = self.ch.getReturnCode()
            if retcode in self.pkgerrs:
                errstr = self.ch.getErrorString()
                self.logger.log(
                    LogPriority.DEBUG,
                    "Failed to get package name because:\n" + errstr)
            else:
                outputstr = self.ch.getOutputString()
                packagename = outputstr

        except Exception:
            raise

        return packagename

    def getInstall(self):
        """return the install command string for the zypper pkg manager


        :returns: string
        @author: Derek Walker
        @change: 12/24/2014 - Breen Malmberg - added method documentation

        """

        return self.install

    def getRemove(self):
        """return the uninstall/remove command string for the zypper pkg manager


        :returns: string
        @author: Derek Walker
        @change: 12/24/2014 - Breen Malmberg - added method documentation

        """

        return self.remove
Beispiel #7
0
class Portage(object):
    '''The template class that provides a framework that must be implemented by
    all platform specific pkgmgr classes.
    
    :version:
    @author: Derek T Walker 08-06-2012
    @change: Breen Malmberg - 4/18/2017 - minor method edits; added doc strings;
            added logging; added parameter validation; removed detailedresults
            reset in __init__ of this class (should only be done in rule template
            and the individual rules); fixed some pep8 grammar; fixed the location
            of emerge (changed 'emerge' to '/usr/bin/emerge')


    '''
    def __init__(self, logger):
        self.logger = logger
        self.ch = CommandHelper(self.logger)
        self.install = "/usr/bin/emerge "
        self.remove = self.install + " --unmerge "
        self.search = self.install + " --search "

###############################################################################

    def installpackage(self, package):
        '''Install a package. Return a bool indicating success or failure.

        :param package: string; Name of the package to be installed, must be
                recognizable to the underlying package manager.
        :returns: installed
        :rtype: bool
@author: Derek T Walker 08-06-2012
@change: Breen Malmberg - 4/18/2017 - fixed return var logic; fixed
        doc string; added parameter validation; changed detailedresults
        to logging

        '''

        installed = False

        try:

            # parameter validation
            if not package:
                self.logger.log(LogPriority.DEBUG,
                                "Parameter: package was blank!")
                return installed
            if not isinstance(package, str):
                self.logger.log(
                    LogPriority.DEBUG,
                    "Parameter: package must be of type string. Got: " +
                    str(type(package)))
                return installed

            self.ch.executeCommand(self.install + package)

            if self.ch.getReturnCode() == 0:
                installed = True
                self.logger.log(
                    LogPriority.DEBUG,
                    "Package " + str(package) + " was successfully installed")
            else:
                self.logger.log(LogPriority.DEBUG,
                                "Failed to install package " + str(package))

        except Exception:
            raise
        return installed

###############################################################################

    def removepackage(self, package):
        '''Remove a package. Return a bool indicating success or failure.

        :param package: string; Name of the package to be removed, must be
                recognizable to the underlying package manager.
        :returns: removed
        :rtype: bool
@author: Derek T Walker 08-6-2012
@change: Breen Malmberg - 4/18/2017 - minor edit; replaced detailedresults
        with logging; fixed return var logic; added parameter validation;
        fixed doc string

        '''

        removed = False

        try:

            # parameter validation
            if not package:
                self.logger.log(LogPriority.DEBUG,
                                "Parameter: package was blank!")
                return removed
            if not isinstance(package, str):
                self.logger.log(
                    LogPriority.DEBUG,
                    "Parameter package must be of type string. Got: " +
                    str(type(package)))
                return removed

            self.ch.executeCommand(self.remove + package)

            if self.ch.getReturnCode() == 0:
                removed = True
                self.logger.log(
                    LogPriority.DEBUG,
                    "Package " + str(package) + " was successfully removed")
            else:
                self.logger.log(LogPriority.DEBUG,
                                "Failed to remove package " + str(package))

        except Exception:
            raise
        return removed

###############################################################################

    def checkInstall(self, package):
        '''Check the installation status of a package. Return a bool;
        True if the package is installed.

        :param package: string; Name of the package whose installation status
                is to be checked, must be recognizable to the underlying package
                manager
        :returns: installed
        :rtype: bool
@author: Derek T. Walker
@change: Breen Malmberg - 4/18/2017 - fixed doc string; minor edit;
        replaced detailedresults with logging (detailedresults should
        be handled in the calling rule); changed name of return var;
        added parameter validation

        '''

        installed = False

        try:

            # parameter validation
            if not package:
                self.logger.log(LogPriority.DEBUG,
                                "Parameter: package was blank!")
                return installed
            if not isinstance(package, str):
                self.logger.log(
                    LogPriority.DEBUG,
                    "Parameter: package must be of type string. Got: " +
                    str(type(package)))
                return installed

            stringToinstalled = "(.*)" + package + "(.*)"

            f = glob.glob('/var/db/pkg')

            for item in f:
                if re.search(stringToinstalled, item):
                    installed = True
                    break

            if installed:
                self.logger.log(LogPriority.DEBUG,
                                "\nPackage " + str(package) + " is installed")
            else:
                self.logger.log(
                    LogPriority.DEBUG,
                    "\nPackage " + str(package) + " is NOT installed")

        except Exception:
            raise
        return installed

###############################################################################

    def checkAvailable(self, package):
        '''check if the specified package is available
        return True if found
        return False if not found

        :param package: string; name of package to check for
        :returns: found
        :rtype: bool
@author: Derek T. Walker
@change: Breen Malmberg - 4/18/2017 - added doc string; minor edit;
        parameter validation added; added code comments; added logging;
        replaced detailedresults with logging

        '''

        available = False

        try:

            # parameter validation
            if not package:
                self.logger.log(LogPriority.DEBUG,
                                "Parameter: package was blank!")
                return available
            if not isinstance(package, str):
                self.logger.log(
                    LogPriority.DEBUG,
                    "Parameter: package must be of type string. Got: " +
                    str(type(package)))
                return available

            self.ch.executeCommand(self.search + package)
            if self.ch.getReturnCode() == 0:
                available = True
                self.logger.log(
                    LogPriority.DEBUG,
                    "Package " + str(package) + " is available to install")
            else:
                self.logger.log(
                    LogPriority.DEBUG,
                    "Package " + str(package) + " is NOT available to install")

        except Exception:
            raise
        return available

    def checkUpdate(self, package=""):
        '''check for updates for the specified package
        or if not package is specified, check for
        all updates on the system

        :param package: string; name of package, for which, to check updates (Default value = "")
        :returns: updatesavail
        :rtype: bool
@author: Breen Malmberg

        '''

        # defaults
        updatesavail = False
        searchpkg = self.search + package + " &> /dev/null"

        try:

            # I have no idea how to check for ALL updates, with portage/emerge..

            if package:
                # parameter validation
                if not isinstance(package, str):
                    self.logger.log(
                        LogPriority.DEBUG,
                        "Parameter package must be of type string. Got: " +
                        str(type(package)))
                    return updatesavail

                # check for updates for specified package
                self.ch.executeCommand(searchpkg)
                if self.ch.getReturnCode() == 100:
                    updatesavail = True
            else:
                self.logger.log(
                    LogPriority.DEBUG,
                    "No package specified. Did NOT search for all updates.")

        except Exception:
            raise
        return updatesavail

    def Update(self, package=""):
        '''update either the specified package or all packages
        on the system, if no package is specified

        :param pacakge: string; name of package to update
        :param package:  (Default value = "")
        :returns: updated
        :rtype: bool
@author: Breen Malmberg

        '''

        # defaults
        updated = True
        updatepkg = self.install + " -avDuN " + package
        updateall = self.install + " -uDU --with-bdeps=y @world"

        try:

            if package:
                self.logger.log(
                    LogPriority.DEBUG,
                    "Attempting to update package " + str(package) + " ...")
                self.ch.executeCommand(updatepkg)
                retcode = self.ch.getReturnCode()
                if retcode != 0:
                    updated = False
                    self.logger.log(LogPriority.DEBUG,
                                    "Failed to update package " + str(package))
                    self.logger.log(LogPriority.DEBUG,
                                    "Error code: " + str(retcode))
            else:
                self.logger.log(LogPriority.DEBUG,
                                "Attempting to update all packages...")
                self.ch.executeCommand(updateall)
                retcode = self.ch.getReturnCode()
                if retcode != 0:
                    updated = False
                    self.logger.log(LogPriority.DEBUG,
                                    "Failed to update packages")
                    self.logger.log(LogPriority.DEBUG,
                                    "Error code: " + str(retcode))

        except Exception:
            raise
        return updated

###############################################################################

    def getInstall(self):
        return self.install

###############################################################################

    def getRemove(self):
        return self.remove

    def getSearch(self):
        return self.search
Beispiel #8
0
class Solaris(object):
    def __init__(self, logger):
        '''Remember, for solaris sparc systems, the package names will typically
            begin with SUNW


        '''
        self.logger = logger
        self.detailedresults = ""
        self.sparc = "(.)*sparc(.)*"
        self.ch = CommandHelper(self.logger)
        self.install = "/usr/sbin/pkgadd -n -i "
        self.remove = "/usr/sbin/pkgrm -n "
        self.info = "/usr/bin/pkginfo "
###############################################################################

    def installpackage(self, package):
        '''Install a package. Return a bool indicating success or failure.

        :param string: package : Name of the package to be installed, must be
            recognizable to the underlying package manager.
        :param package: 
        :returns: bool :
        @author

        '''
        try:
            #             retval = call(self.install + package,stdout=None,shell=True)
            #             if retval == 0:
            self.ch.executeCommand(self.install + package)
            if self.ch.getReturnCode() == 0:
                self.detailedresults = package + " pkg installed successfully"
                self.logger.log(LogPriority.INFO,
                                ["Solaris.install", self.detailedresults])
                return True
            else:
                self.detailedresults = package + " pkg not able to install."
                self.detailedresults += "This package may not be available, \
                may be mispelled, or may depend on other packages in which non\
                interactive mode can't be used"

                self.logger.log(LogPriority.INFO,
                                ["Solaris.install", self.detailedresults])
                return False
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception as err:
            #print err
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.INFO,
                            ['Solaris.install', self.detailedresults])
###############################################################################

    def removepackage(self, package):
        '''Remove a package. Return a bool indicating success or failure.

        :param string: package : Name of the package to be removed, must be
            recognizable to the underlying package manager.
        :param package: 
        :returns: bool :
        @author

        '''

        try:
            #             retval = call(self.remove + package,stdout=None,shell=True)
            #             if retval == 0:
            self.ch.executeCommand(self.remove + package)
            if self.ch.getReturnCode() == 0:
                self.detailedresults = package + " pkg removed successfully"
                self.logger.log(LogPriority.INFO,
                                ["Solaris.remove", self.detailedresults])
                return True
            else:
                self.detailedresults = package + " pkg not able to be removed."
                self.detailedresults += "This package may not be installed \
                may be mispelled or may depend on other packages in which non \
                interactive mode can't be used"

                self.logger.log(LogPriority.INFO,
                                ["Solaris.remove", self.detailedresults])
                return False
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception as err:
            #print err
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.INFO,
                            ["Solaris.remove", self.detailedresults])
###############################################################################

    def checkInstall(self, package):
        '''Check the installation status of a package. Return a bool; True if
        the packageis installed.

        :param string: package : Name of the package whose installation status
            is to be checked, must be recognizable to the underlying package
            manager.
        :param package: 
        :returns: bool :
        @author

        '''
        try:
            if search("^SUNW", package):
                retval = call(["/usr/bin/pkginfo", package],
                              stdout=None,
                              shell=False)
            else:
                retval = call("/usr/bin/pkginfo | grep -i " + package,
                              stdout=None,
                              shell=True)
            if retval == 0:
                self.detailedresults = package + " pkg found"
                self.logger.log(LogPriority.INFO,
                                ["Solaris.check", self.detailedresults])
                return True
            else:
                self.detailedresults = package + " pkg not found"
                self.logger.log(LogPriority.INFO,
                                ["Solaris.check", self.detailedresults])
                return False
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception as err:
            #print err
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.INFO,
                            ["Solaris.check", self.detailedresults])
###############################################################################

    def checkAvailable(self, package):
        pass
###############################################################################

    def getInstall(self):
        return self.install
###############################################################################

    def getRemove(self):
        return self.remove
Beispiel #9
0
class Yum(object):
    """The template class that provides a framework that must be implemented by
    all platform specific pkgmgr classes.

    """

    def __init__(self, logger):
        self.environ = Environment()
        self.logger = logger
        self.ch = CommandHelper(self.logger)
        self.yumloc = "/usr/bin/yum"
        self.install = self.yumloc + " install -y "
        self.remove = self.yumloc + " remove -y "
        self.search = self.yumloc + " list "
        self.checkupdates = self.search + "updates "
        self.listavail = self.search + "available "
        self.listinstalled = self.search + "installed "
        self.updatepkg = self.yumloc + " update -y --obsoletes "
        myos = self.environ.getostype().lower()
        if re.search("red hat.*?release 6", myos) or \
                re.search("^centos$", myos.strip()):
            self.rpmloc = "/bin/rpm"
        else:
            self.rpmloc = "/usr/bin/rpm"
        self.provides = self.rpmloc + " -qf "
        self.query = self.rpmloc + " -qa "

    def installpackage(self, package):
        """Install a package. Return a bool indicating success or failure.

        :param package: string; Name of the package to be installed, must be
                recognizable to the underlying package manager.
        :return: installed
        :rtype: bool

        """

        installed = True
        maxtries = 12
        trynum = 0

        while psRunning("yum"):
            trynum += 1
            if trynum == maxtries:
                self.logger.log(LogPriority.DEBUG, "Timed out while attempting to install package due to yum package manager being in-use by another process.")
                installed = False
                return installed
            else:
                self.logger.log(LogPriority.DEBUG, "Yum package manager is in-use by another process. Waiting for it to be freed...")
                time.sleep(5)

        try:

            self.ch.executeCommand(self.install + package)
            retcode = self.ch.getReturnCode()

            if retcode != 0:
                errstr = self.ch.getErrorString()
                self.logger.log(LogPriority.DEBUG, "Yum command failed with: " + str(errstr))
                installed = False

            if installed:
                self.logger.log(LogPriority.DEBUG, "Package " + str(package) + " was installed successfully")
            else:
                self.logger.log(LogPriority.DEBUG, "Failed to install package " + str(package))

        except Exception:
            raise
        return installed

    def removepackage(self, package):
        """Remove a package. Return a bool indicating success or failure.

        :param package: string; Name of the package to be removed, must be
                recognizable to the underlying package manager.
        :return: removed
        :rtype: bool

        """

        removed = True
        maxtries = 12
        trynum = 0

        while psRunning("yum"):
            trynum += 1
            if trynum == maxtries:
                self.logger.log(LogPriority.DEBUG, "Timed out while attempting to remove package, due to yum package manager being in-use by another process.")
                removed = False
                return removed
            else:
                self.logger.log(LogPriority.DEBUG, "Yum package manager is in-use by another process. Waiting for it to be freed...")
                time.sleep(5)

        try:

            self.ch.executeCommand(self.remove + package)
            retcode = self.ch.getReturnCode()
            if retcode != 0:
                errstr = self.ch.getErrorString()
                self.logger.log(LogPriority.DEBUG, "Yum command failed with: " + str(errstr))
                removed = False

            if removed:
                self.logger.log(LogPriority.DEBUG, "Package " + str(package) + " was successfully removed")
            else:
                self.logger.log(LogPriority.DEBUG, "Failed to remove package " + str(package))

        except Exception:
            raise
        return removed

    def checkInstall(self, package):
        """Check the installation status of a package. Return a bool; True if
        the package is installed.

        :param package: string; Name of the package whose installation status
            is to be checked, must be recognizable to the underlying package
            manager.
        :return: found
        :rtype: bool

        """

        installed = True
        maxtries = 12
        trynum = 0
        acceptablecodes = [1,0]

        while psRunning("yum"):
            trynum += 1
            if trynum == maxtries:
                self.logger.log(LogPriority.DEBUG, "Timed out while attempting to check status of package, due to yum package manager being in-use by another process.")
                installed = False
                return installed
            else:
                self.logger.log(LogPriority.DEBUG, "Yum package manager is in-use by another process. Waiting for it to be freed...")
                time.sleep(5)

        try:

            self.ch.executeCommand(self.listinstalled + package)
            retcode = self.ch.getReturnCode()
            if retcode not in acceptablecodes:
                installed = False
                errmsg = self.ch.getErrorString()
                self.logger.log(LogPriority.DEBUG, "Yum command failed with: " + str(errmsg))
            else:
                outputlines = self.ch.getAllList()
                for line in outputlines:
                    if re.search("No matching Packages", line, re.I):
                        installed = False

            if installed:
                self.logger.log(LogPriority.DEBUG, "Package " + str(package) + " is installed")
            else:
                self.logger.log(LogPriority.DEBUG, "Package " + str(package) + " is NOT installed")

        except Exception:
            raise
        return installed

    def Update(self, package=""):
        """update specified package if any updates
        are available for it
        if no package is specified, update all
        packages which can be updated on the system

        :param package: string; name of package to update (Default value = "")
        :return: updated
        :rtype: bool

        """

        updated = True

        try:

            try:
                self.ch.executeCommand(self.updatepkg + package)
                retcode = self.ch.getReturnCode()
                errstr = self.ch.getErrorString()
                if retcode != 0:
                    updated = False
                    raise repoError('yum', retcode, str(errstr))
            except repoError as repoerr:
                if not repoerr.success:
                    self.logger.log(LogPriority.WARNING, str(errstr))
                    updated = False

            if package:
                if updated:
                    self.logger.log(LogPriority.DEBUG, "Package " + str(package) + " was successfully updated")
                else:
                    self.logger.log(LogPriority.DEBUG, "No updates were found for package " + str(package))
            else:
                if updated:
                    self.logger.log(LogPriority.DEBUG, "All packages were successfully updated")
                else:
                    self.logger.log(LogPriority.DEBUG, "No updates were found for this system")

        except Exception:
            raise
        return updated

    def checkUpdate(self, package=""):
        """check if there are any updates available for
        specified package
        if no package is specified, check if any updates
        are available for the current system

        :param package: string; name of package to check (Default value = "")
        :return: updatesavail
        :rtype: bool

        """

        updatesavail = False

        try:

            try:
                self.ch.executeCommand(self.checkupdates + package)
                retcode = self.ch.getReturnCode()
                output = self.ch.getOutputString()
                errstr = self.ch.getErrorString()
                if retcode != 0:
                    raise repoError('yum', retcode, str(errstr))
                else:
                    if re.search("Updated packages", output, re.IGNORECASE):
                        updatesavail = True
            except repoError as repoerr:
                if not repoerr.success:
                    self.logger.log(LogPriority.WARNING, str(errstr))
                else:
                    if re.search("Updated packages", output, re.IGNORECASE):
                        updatesavail = True

            if package:
                if updatesavail:
                    self.logger.log(LogPriority.DEBUG, "Updates are available for package " + str(package))
                else:
                    self.logger.log(LogPriority.DEBUG, "No updates are available for package " + str(package))
            else:
                if updatesavail:
                    self.logger.log(LogPriority.DEBUG, "Updates are available for this system")
                else:
                    self.logger.log(LogPriority.DEBUG, "No updates are available for this system")

        except Exception:
            raise
        return updatesavail

    def checkAvailable(self, package):
        """check if specified package is available to install
        return True if it is
        return False if not

        :param package: string; name of package to check
        :return: available
        :rtype: bool

        """

        available = True
        maxtries = 12
        trynum = 0

        while psRunning("yum"):
            trynum += 1
            if trynum == maxtries:
                self.logger.log(LogPriority.DEBUG, "Timed out while attempting to check availability of package, due to yum package manager being in-use by another process.")
                available = False
                return available
            else:
                self.logger.log(LogPriority.DEBUG, "Yum package manager is in-use by another process. Waiting for it to be freed...")
                time.sleep(5)

        try:

            self.ch.executeCommand(self.listavail + package)
            retcode = self.ch.getReturnCode()
            if retcode in [0,1]:
                output = self.ch.getAllList()
                for line in output:
                    if re.search("No matching Packages", line, re.I):
                        available = False
            else:
                errstr = self.ch.getErrorString()
                available = False
                self.logger.log(LogPriority.DEBUG, errstr)

            if available:
                self.logger.log(LogPriority.DEBUG, "Package " + str(package) + " is available to install")
            else:
                self.logger.log(LogPriority.DEBUG, "No package " + str(package) + " was found to install")

        except Exception:
            raise
        return available

    def getPackageFromFile(self, filename):
        """Returns the name of the package that provides the given
        filename/path.

        :param filename: string; The name or path of the file to resolve
        :return: packagename
        :rtype: string

        """

        packagename = ""

        try:

            try:
                self.ch.executeCommand(self.provides + filename)
                retcode = self.ch.getReturnCode()
                outputstr = self.ch.getOutputString()
                errstr = self.ch.getErrorString()
                if retcode != 0:
                    raise repoError('yum', retcode, str(errstr))
                else:
                    packagename = outputstr
            except repoError as repoerr:
                if not repoerr.success:
                    self.logger.log(LogPriority.WARNING, str(errstr))

        except Exception:
            raise
        return packagename

    def getInstall(self):
        return self.install

    def getRemove(self):
        return self.remove
Beispiel #10
0
class LaunchCtl(object):
    '''Service manager that provides an interface to the Mac OS launchctl command.
    
    @privatemethod: validateSubCommand - validate a command that is
                    formatted as: { <subcommand> : [<arg1>, <arg1>, <arg3>]}
                    where each argN is a string.
    
    @privatemethod: runSubCommand - runs a launchctl command, in the format
                    described above, then collects standard out, standard
                    error and the launchctl return code, returning them to the
                    caller.
    
    # -------------------------------------------------------------------------
    Legacy commands
    
    @publicmethod: load - (legacy) loads the passed in plist/service
    
    @publicmethod: unload - (legacy) unloads the passed in plist/service
    
    @publicmethod: start - (legacy) Start a service
    
    @publicmethod: stop - (legacy) Stop a service
    
    @publicmethod: list - (legacy) list a specific plist state, or
                   returns a list of running launchd services.
    
    @publicmethod: bsexec - (legacy) execute a command in as close as possible
                            context to the passed in PID.
    
    @publicmethod: asuser - (legacy) execute a command in as close as possible
                            context to the passed in UID
    
    # -------------------------------------------------------------------------
    Current commands
    
    @publicmethod: bootstrap
    
    @publicmethod: bootout
    
    @publicmethod: enable
    
    @publicmethod: disable
    
    @publicmethod: uncache
    
    @publicmethod: kickstart
    
    @publicmethod: kill - (2.0) Kills a service, with one of the passed
                   in signals that are described in the Mac OS signal(3)
                   manpage.
    
    @publicmethod: blame
    
    @publicmethod: printTarget
    
    @publicmethod: printCache
    
    @publicmethod: printDisabled
    
    @publicmethod: procinfo
    
    @publicmethod: hostinfo
    
    @publicmethod: resolveport
    
    @publicmethod: reboot
    
    @Note: Future subcommands may include 'plist', 'config', 'error'.
    
    @author: Roy Nielsen


    '''
    def __init__(self, logger):
        """
        Initialization Method

        @author: Roy Nielsen
        """
        self.launchctl = "/bin/launchctl"
        self.logger = logger
        self.ch = CommandHelper(self.logger)

    # ----------------------------------------------------------------------
    # helper methods
    # ----------------------------------------------------------------------

    def isSaneFilePath(self, filepath):
        '''Check for a good file path in the passed in string.
        
        @author: Roy Nielsen

        :param filepath: 

        '''
        sane = False
        if isinstance(filepath, str):
            if re.match("^[A-Za-z/\.][A-Za-z0-9/\._-]*", filepath):
                sane = True
            else:
                self.logger.log(
                    lp.DEBUG, "filepath: " + str(filepath) + " is not valid.")
        return sane

    # ----------------------------------------------------------------------

    def validateSubCommand(self, command={}):
        '''Validate that we have a properly formatted command, and the subcommand
        is valid.

        :param command:  (Default value = {})
        :returns: s: success - whether the command was formatted correctly or not.
        
        @author: Roy Nielsen

        '''
        success = False
        subcmd = []
        if not isinstance(command, dict):
            self.logger.log(lp.ERROR, "Command must be a dictionary...")
        else:
            commands = 0
            for subCommand, args in list(command.items()):
                commands += 1
                #####
                # Check to make sure only one command is in the dictionary
                if commands > 1:
                    self.logger.log(
                        lp.ERROR, "Damn it Jim! One command at " + "a time!!")
                    success = False
                    break
                #####
                # Check if the subcommand is a valid subcommand...
                validSubcommands = [
                    "load", "unload", "start", "stop", "list", "bsexec",
                    "asuser", "bootstrap", "bootout", "enable", "disable",
                    "uncache", "kickstart", "kill", "blame", "print",
                    "print-cache", "print-disabled", "procinfo", "hostinfo",
                    "resolveport", "reboot"
                ]
                if subCommand not in validSubcommands:
                    success = False
                    break
                else:
                    success = True
                #####
                # Check to make sure the key or subCommand is a string, and
                # the value is alist and args are
                if not isinstance(subCommand, str) or \
                   not isinstance(args, list):
                    self.logger.log(
                        lp.ERROR, "subcommand needs to be a " +
                        "string, and args needs to be a list " + "of strings")
                    success = False
                else:
                    #####
                    # Check the arguments to make sure they are all strings
                    for arg in args:
                        if not isinstance(arg, str):
                            self.logger.log(
                                lp.ERROR, "Arg '" + str(arg) +
                                "'needs to be a string...")
                            success = False
                    if success:
                        subcmd = [subCommand] + args
                        self.logger.log(lp.DEBUG, 'subcmd: ' + str(subcmd))
        return success, subcmd

    # -------------------------------------------------------------------------

    def runSubCommand(self, commandDict={}):
        '''Use the passed in dictionary to create a MacOS 'security' command
        and execute it.

        :param commandDict:  (Default value = {})
        :returns: s: success - whether the command was successfull or not.
        
        @author: Roy Nielsen

        '''
        success = False
        output = ''
        error = ''
        returncode = ''
        #####
        # Make sure the command dictionary was properly formed, as well as
        # returning the formatted subcommand list
        validationSuccess, subCmd = self.validateSubCommand(commandDict)
        if validationSuccess:
            #####
            # Command setup - note that the keychain deliberately has quotes
            # around it - there could be spaces in the path to the keychain,
            # so the quotes are required to fully resolve the file path.
            # Note: this is done in the build of the command, rather than
            # the build of the variable.
            cmd = [self.launchctl] + subCmd
            self.logger.log(lp.DEBUG, 'cmd: ' + str(cmd))
            #####
            # set up and run the command
            # self.ch.setCommand(cmd)
            success = self.ch.executeCommand(cmd)

            output = self.ch.getOutput()
            error = self.ch.getError()
            returncode = self.ch.getReturnCode()
            #####
            # If the return code is 0, then we have success.
            if not returncode:
                success = True
                """
                if "bootstrap" in subCmd:
                    raise ValueError("cmd: " + str(cmd) + " output: " +
                                     str(output) + " error: " + str(error) +
                                     " retcode: " + str(returncode))
                """

            if error:
                self.logger.log(lp.INFO, "Output: " + str(output))
                self.logger.log(lp.INFO, "Error: " + str(error))
                self.logger.log(lp.INFO, "Return code: " + str(returncode))
                success = False

        else:
            raise ValueError

        return success, str(output), str(error), str(returncode)

    # ----------------------------------------------------------------------
    # Legacy Subcommands
    # ----------------------------------------------------------------------

    def load(self, plist="", options="", sessionType="", domain=False):
        '''@note: From the launchctl man page:
          load | unload [-wF] [-S sessiontype] [-D domain] paths ...
              Load the specified configuration files or directories of con-
              figuration files.  Jobs that are not on-demand will be started
              as soon as possible. All specified jobs will be loaded before
              any of them are allowed to start. Note that per-user configura-
              tion files (LaunchAgents) must be owned by root (if they are
              located in /Library/LaunchAgents) or the user loading them (if
              they are located in $HOME/Library/LaunchAgents).  All system-
              wide daemons (LaunchDaemons) must be owned by root. Configura-
              tion files must disallow group and world writes. These restric-
              tions are in place for security reasons, as allowing writabil-
              ity to a launchd configuration file allows one to specify which
              executable will be launched.
        
              Note that allowing non-root write access to the
              /System/Library/LaunchDaemons directory WILL render your system
              unbootable.
        
              -w       Overrides the Disabled key and sets it to false or
                       true for the load and unload subcommands respectively.
                       In previous versions, this option would modify the
                       configuration file. Now the state of the Disabled key
                       is stored elsewhere on- disk in a location that may
                       not be directly manipulated by any process other than
                       launchd.
        
              -F       Force the loading or unloading of the plist. Ignore
                       the Disabled key.
        
              -S sessiontype
                       Some jobs only make sense in certain contexts. This
                       flag instructs launchctl to look for jobs in a differ-
                       ent location when using the -D flag, and allows
                       launchctl to restrict which jobs are loaded into which
                       session types. Sessions are only relevant for per-user
                       launchd contexts. Relevant sessions are Aqua (the
                       default), Background and LoginWindow.  Background
                       agents may be loaded independently of a GUI login.
                       Aqua agents are loaded only when a user has logged in
                       at the GUI. LoginWindow agents are loaded when the
                       LoginWindow UI is displaying and currently run as
                       root.
        
              -D domain
                       Look for plist(5) files ending in *.plist in the
                       domain given. This option may be thoughts of as
                       expanding into many individual paths depending on the
                       domain name given. Valid domains include "system,"
                       "local," "network" and "all." When providing a session
                       type, an additional domain is available for use called
                       "user." For example, without a session type given, "-D
                       system" would load from or unload property list files
                       from /System/Library/LaunchDaemons.  With a session
                       type passed, it would load from /System/Library/Laun-
        
              NOTE: Due to bugs in the previous implementation and long-
              standing client expectations around those bugs, the load and
              unload subcommands will only return a non-zero exit code due to
              improper usage.  Otherwise, zero is always returned.
        
        @author: Roy Nielsen

        :param plist:  (Default value = "")
        :param options:  (Default value = "")
        :param sessionType:  (Default value = "")
        :param domain:  (Default value = False)

        '''
        success = False
        #####
        # Input validation.
        if self.isSaneFilePath(plist):
            args = []

            if re.match("[-wF]+", str(options)) and \
               isinstance(options, str):
                args.append(options)
            else:
                self.logger.log(
                    lp.INFO,
                    "Need a the options to be a single" + " string...")

            sessionTypes = ['Aqua', 'StandardIO', 'Background', 'LoginWindow']
            if sessionType in sessionTypes:
                args += ['-S', sessionType]
            else:
                self.logger.log(
                    lp.INFO, "Need a the sessionType in: " + str(sessionTypes))

            if isinstance(domain, str):
                args += ['-D', domain]
            else:
                self.logger.log(lp.INFO,
                                "Need a the domain in: " + str(sessionTypes))

            args.append(plist)

            cmd = {"load": args}
            success, _, stderr, _ = self.runSubCommand(cmd)

            if not success and re.search("already loaded", stderr):
                success = True

        return success

    # -------------------------------------------------------------------------

    def unLoad(self, plist="", options="", sessionType="", domain=False):
        '''@note: From the launchctl man page:
          load | unload [-wF] [-S sessiontype] [-D domain] paths ...
              Load the specified configuration files or directories of con-
              figuration files.  Jobs that are not on-demand will be started
              as soon as possible. All specified jobs will be loaded before
              any of them are allowed to start. Note that per-user configura-
              tion files (LaunchAgents) must be owned by root (if they are
              located in /Library/LaunchAgents) or the user loading them (if
              they are located in $HOME/Library/LaunchAgents).  All system-
              wide daemons (LaunchDaemons) must be owned by root. Configura-
              tion files must disallow group and world writes. These restric-
              tions are in place for security reasons, as allowing writabil-
              ity to a launchd configuration file allows one to specify which
              executable will be launched.
        
              Note that allowing non-root write access to the
              /System/Library/LaunchDaemons directory WILL render your system
              unbootable.
        
              -w       Overrides the Disabled key and sets it to false or
                       true for the load and unload subcommands respectively.
                       In previous versions, this option would modify the
                       configuration file. Now the state of the Disabled key
                       is stored elsewhere on- disk in a location that may
                       not be directly manipulated by any process other than
                       launchd.
        
              -F       Force the loading or unloading of the plist. Ignore
                       the Disabled key.
        
              -S sessiontype
                       Some jobs only make sense in certain contexts. This
                       flag instructs launchctl to look for jobs in a differ-
                       ent location when using the -D flag, and allows
                       launchctl to restrict which jobs are loaded into which
                       session types. Sessions are only relevant for per-user
                       launchd contexts. Relevant sessions are Aqua (the
                       default), Background and LoginWindow.  Background
                       agents may be loaded independently of a GUI login.
                       Aqua agents are loaded only when a user has logged in
                       at the GUI. LoginWindow agents are loaded when the
                       LoginWindow UI is displaying and currently run as
                       root.
        
              -D domain
                       Look for plist(5) files ending in *.plist in the
                       domain given. This option may be thoughts of as
                       expanding into many individual paths depending on the
                       domain name given. Valid domains include "system,"
                       "local," "network" and "all." When providing a session
                       type, an additional domain is available for use called
                       "user." For example, without a session type given, "-D
                       system" would load from or unload property list files
                       from /System/Library/LaunchDaemons.  With a session
                       type passed, it would load from /System/Library/Laun-
        
              NOTE: Due to bugs in the previous implementation and long-
              standing client expectations around those bugs, the load and
              unload subcommands will only return a non-zero exit code due to
              improper usage.  Otherwise, zero is always returned.
        
        @author: Roy Nielsen

        :param plist:  (Default value = "")
        :param options:  (Default value = "")
        :param sessionType:  (Default value = "")
        :param domain:  (Default value = False)

        '''
        success = False
        #####
        # Input validation.
        if self.isSaneFilePath(plist):
            args = []

            if re.match("[-wF]+", str(options)) and \
               isinstance(options, str):
                args.append(options)
            else:
                self.logger.log(
                    lp.INFO,
                    "Need a the options to be a single" + " string...")

            sessionTypes = ['Aqua', 'StandardIO', 'Background', 'LoginWindow']
            if sessionType in sessionTypes:
                args += ['-S', sessionType]
            else:
                self.logger.log(
                    lp.INFO, "Need a the sessionType in: " + str(sessionTypes))

            if isinstance(domain, str):
                args += ['-D', domain]
            else:
                self.logger.log(lp.INFO,
                                "Need a the domain in: " + str(sessionTypes))

            args.append(plist)

            cmd = {"unload": args}
            success, _, stderr, _ = self.runSubCommand(cmd)
            if not success and re.search('Could not find specified', stderr):
                success = True

        return success

    # -------------------------------------------------------------------------

    def start(self, label=""):
        '''@note: From the launchctl man page:
          start label
              Start the specified job by label. The expected use of this sub-
              command is for debugging and testing so that one can manually
              kick-start an on-demand server.
        
        @author: Roy Nielsen

        :param label:  (Default value = "")

        '''
        success = False
        #####
        # Input validation.
        if not label or not isinstance(label, str):
            return success

        cmd = {"start": label}
        success, _, _, _ = self.runSubCommand(cmd)

        return success

    # -------------------------------------------------------------------------

    def stop(self, label=""):
        '''@note: From the launchctl man page:
          stop label
              Stop the specified job by label. If a job is on-demand, launchd
              may immediately restart the job if launchd finds any criteria
              that is satisfied.
        
        @author: Roy Nielsen

        :param label:  (Default value = "")

        '''
        success = False
        #####
        # Input validation.
        if not label or not isinstance(label, str):
            return success

        cmd = {"stop": label}
        success, _, _, _ = self.runSubCommand(cmd)

        return success

    # -------------------------------------------------------------------------

    def list(self, label=""):
        '''@note: From the launchctl man page:
          list [-x] [label]
              With no arguments, list all of the jobs loaded into launchd in
              three columns. The first column displays the PID of the job if
              it is running.  The second column displays the last exit status
              of the job. If the number in this column is negative, it repre-
              sents the negative of the signal which stopped the job. Thus,
              "-15" would indicate that the job was terminated with SIGTERM.
              The third column is the job's label. If [label] is specified,
              prints information about the requested job.
        
              -x       This flag is no longer supported.
        
        @author: Roy Nielsen

        :param label:  (Default value = "")

        '''
        success = False
        #####
        # Input validation.
        if label and isinstance(label, str):
            cmd = [self.launchctl, 'list', label]
        elif not label:
            cmd = [self.launchctl, 'list']
        else:
            return success
        #####
        # set up and run the command
        # self.ch.setCommand(cmd)
        success = self.ch.executeCommand(cmd)

        output = self.ch.getOutput()
        error = self.ch.getError()
        returncode = self.ch.getReturnCode()

        if not error:
            self.logger.log(lp.DEBUG, "Output: " + str(output))
            self.logger.log(lp.DEBUG, "Error: " + str(error))
            self.logger.log(lp.DEBUG, "Return code: " + str(returncode))
            success = True
        else:
            self.logger.log(lp.DEBUG, "Output: " + str(output))
            self.logger.log(lp.DEBUG, "Error: " + str(error))
            self.logger.log(lp.DEBUG, "Return code: " + str(returncode))
            success = False

        return success, output, error, returncode

    # -------------------------------------------------------------------------

    def bsExec(self, pid, command, args=[]):
        '''@note: From the launchctl man page:
          bsexec PID command [args]
              This executes the given command in as similar an execution con-
              text as possible to the target PID. Adopted attributes include
              the Mach bootstrap namespace, exception server and security
              audit session. It does not modify the process' credentials
              (UID, GID, etc.) or adopt any environment variables from the
              target process. It affects only the Mach bootstrap context and
              directly-related attributes.
        
        @author: Roy Nielsen

        :param pid: 
        :param command: 
        :param args:  (Default value = [])

        '''
        success = False
        #####
        # Input validation.
        if not isinstance(pid, int) or \
           not isinstance(command, str) or \
           not isinstance(args, list):
            return success

        cmd = {"bsexec": [pid, command] + args}
        success, _, _, _ = self.runSubCommand(cmd)

        return success

    # -------------------------------------------------------------------------

    def asUser(self, uid, command, args=[]):
        '''@note: From the launchctl man page:
          asuser UID command [args]
              This executes the given command in as similar an execution con-
              text as possible to that of the target user's bootstrap.
              Adopted attributes include the Mach bootstrap namespace, excep-
              tion server and security audit session. It does not modify the
              process' credentials (UID, GID, etc.) or adopt any user-spe-
              cific environment variables. It affects only the Mach bootstrap
              context and directly- related attributes.
        
        @author: Roy Nielsen

        :param uid: 
        :param command: 
        :param args:  (Default value = [])

        '''
        success = False
        #####
        # Input validation.
        if not isinstance(uid, int) or \
           not isinstance(command, str) or \
           not isinstance(args, list):
            return success

        cmd = {"asuser": [uid, command] + args}
        success, stdout, stderr, retcode = self.runSubCommand(cmd)

        if retcode != '0':
            raise ValueError(reportStack() + "- success: " + str(success) +
                             " stdout: " + str(stdout) + " stderr: " +
                             str(stderr) + " retcode: " + str(retcode))
        return success

    # ----------------------------------------------------------------------
    # Supported Second generation subcommands
    # ----------------------------------------------------------------------

    def bootStrap(self, domainTarget="", servicePath=''):
        '''@note: From the launchctl man page:
          bootstrap | bootout domain-target [service-path service-path2 ...] |
              service-target
              Bootstraps or removes domains and services. Services may be
              specified as a series of paths or a service identifier. Paths
              may point to XPC service bundles, launchd.plist(5) s, or a
              directories containing a collection of either. If there were
              one or more errors while bootstrapping or removing a collection
              of services, the problematic paths will be printed with the
              errors that occurred.
        
              If no paths or service target are specified, these commands can
              either bootstrap or remove a domain specified as a domain tar-
              get. Some domains will implicitly bootstrap pre-defined paths
              as part of their creation.
        
        @author: Roy Nielsen

        :param domainTarget:  (Default value = "")
        :param servicePath:  (Default value = '')

        '''
        success = False
        cmd = ''
        #####
        # Input validation.
        if not isinstance(domainTarget, str) or \
           not isinstance(servicePath, str):
            return success

        if servicePath and domainTarget:
            cmd = {"bootstrap": [domainTarget, servicePath]}
        elif domainTarget:
            cmd = {"bootstrap": [domainTarget]}
        else:
            return success

        success, stdout, stderr, retcode = self.runSubCommand(cmd)

        if retcode != '0':
            self.logger.log(
                lp.DEBUG,
                reportStack() + "- success: " + str(success) + " stdout: " +
                str(stdout) + " stderr: " + str(stderr) + " retcode: " +
                str(retcode))
        return success

    # ----------------------------------------------------------------------

    def bootOut(self, domainTarget="", servicePath=''):
        '''@note: From the launchctl man page:
          bootstrap | bootout domain-target [service-path service-path2 ...] |
              service-target
              Bootstraps or removes domains and services. Services may be
              specified as a series of paths or a service identifier. Paths
              may point to XPC service bundles, launchd.plist(5) s, or a
              directories containing a collection of either. If there were
              one or more errors while bootstrapping or removing a collection
              of services, the problematic paths will be printed with the
              errors that occurred.
        
              If no paths or service target are specified, these commands can
              either bootstrap or remove a domain specified as a domain tar-
              get. Some domains will implicitly bootstrap pre-defined paths
              as part of their creation.
        
        @author: Roy Nielsen

        :param domainTarget:  (Default value = "")
        :param servicePath:  (Default value = '')

        '''
        success = False
        #####
        # Input validation.
        if not isinstance(domainTarget, str) or \
           not isinstance(servicePath, str):
            return success

        if servicePath and domainTarget:
            cmd = {"bootout": [domainTarget, servicePath]}
        elif domainTarget:
            cmd = {"bootout": [domainTarget]}
        else:
            return success

        success, stdout, stderr, retcode = self.runSubCommand(cmd)
        #####
        # errors that indicate the process is complete or in
        # progress
        if re.search("No such process", stderr) or \
           re.search("Operation now in progress", stderr):
            success = True

        if retcode != '0' and not success:
            self.logger.log(
                lp.DEBUG,
                reportStack() + "- success: " + str(success) + " stdout: " +
                str(stdout) + " stderr: " + str(stderr) + " retcode: " +
                str(retcode))
        for item in stderr:
            if item and re.search("Could not find specified service", item):
                success = True
                break
        return success

    # ----------------------------------------------------------------------

    def enable(self, serviceTarget, servicePath=''):
        '''From the launchctl man page:
          enable | disable service-target
              Enables or disables the service in the requested domain. Once a
              service is disabled, it cannot be loaded in the specified
              domain until it is once again enabled. This state persists
              across boots of the device. This subcommand may only target
              services within the system domain or user and user-login
              domains.

        :param serviceTarget: 
        :param servicePath:  (Default value = '')

        '''
        success = False
        #####
        # Input validation.
        if not isinstance(serviceTarget, str):
            return success

        if servicePath and isinstance(servicePath, str):
            cmd = {"enable": [serviceTarget, servicePath]}
        else:
            cmd = {"enable": [serviceTarget]}

        success, stdout, stderr, retcode = self.runSubCommand(cmd)
        if str(retcode) != '0':
            success = False
            self.logger.log(
                lp.DEBUG,
                reportStack() + "- success: " + str(success) + " stdout: " +
                str(stdout) + " stderr: " + str(stderr) + " retcode: " +
                str(retcode))
        else:
            self.logger.log(
                lp.DEBUG,
                reportStack() + "- success: " + str(success) + " stdout: " +
                str(stdout) + " stderr: " + str(stderr) + " retcode: " +
                str(retcode))
            success = True
        return success

    # -------------------------------------------------------------------------

    def disable(self, serviceTarget):
        '''From the launchctl man page:
          enable | disable service-target
              Enables or disables the service in the requested domain. Once a
              service is disabled, it cannot be loaded in the specified
              domain until it is once again enabled. This state persists
              across boots of the device. This subcommand may only target
              services within the system domain or user and user-login
              domains.

        :param serviceTarget: 

        '''
        success = False
        #####
        # Input validation.
        if not isinstance(serviceTarget, str):
            return success

        cmd = {"disable": [serviceTarget]}
        success, stdout, stderr, retcode = self.runSubCommand(cmd)

        if str(retcode) != '0':
            success = False
            self.logger.log(
                lp.DEBUG,
                reportStack() + "- success: " + str(success) + " stdout: " +
                str(stdout) + " stderr: " + str(stderr) + " retcode: " +
                str(retcode))
        else:
            self.logger.log(
                lp.DEBUG,
                reportStack() + "- success: " + str(success) + " stdout: " +
                str(stdout) + " stderr: " + str(stderr) + " retcode: " +
                str(retcode))
            success = True
        return success

    #-------------------------------------------------------------------------

    def unCache(self, serviceName):
        '''Bypass the cache and read the service configuration from disk

        :param serviceName: 

        '''
        success = False
        #####
        # Input validation.
        if not isinstance(serviceName, str):
            return success

        cmd = {"uncache": [serviceName]}
        success, stdout, stderr, retcode = self.runSubCommand(cmd)

        if str(retcode) != '0':
            success = False
            self.logger.log(
                lp.DEBUG,
                reportStack() + "- success: " + str(success) + " stdout: " +
                str(stdout) + " stderr: " + str(stderr) + " retcode: " +
                str(retcode))
        else:
            self.logger.log(
                lp.DEBUG,
                reportStack() + "- success: " + str(success) + " stdout: " +
                str(stdout) + " stderr: " + str(stderr) + " retcode: " +
                str(retcode))
            success = True
        return success

    # -------------------------------------------------------------------------

    def kickStart(self, serviceTarget="", options='-k'):
        '''From the launchctl man page:
          kickstart [-kp] service-target
              Instructs launchd to kickstart the specified service.
              Options can be one of:
        
              -k       If the service is already running, kill the running
                       instance before restarting the service.
        
              -p       Upon success, print the PID of the new process or the
                       already-running process to stdout.
        
            High sierra options:
              -s       Force the service to start.
        
              -x       Attach to xpcproxy(3) before it execs and becomes the
                       service process. This flag is generally not useful
                       for anyone but the launchd maintainer.
        
              (-p)     No longer available in High Sierra

        :param serviceTarget:  (Default value = "")
        :param options:  (Default value = '-k')

        '''
        #####
        # Input validation.
        args = []
        if re.match("[-kp]+", str(options)) and \
           isinstance(options, str):
            args.append(options)
        else:
            self.logger.log(lp.INFO,
                            "Need a the options to be a single " + "string...")

        args.append(serviceTarget)

        self.logger.log(lp.DEBUG, "args: " + str(args))

        cmd = {"kickstart": args}
        self.logger.log(lp.DEBUG, "cmd: " + str(cmd))
        success, stdout, stderr, retcode = self.runSubCommand(cmd)
        #####
        # If a '0' is returned
        if retcode == '0' and success:
            success = True
        else:
            raise ValueError("kickstart - success: " + str(success) +
                             " stdout: " + str(stdout) + " stderr: " +
                             str(stderr) + " retcode: " + str(retcode))

        return success

    # -------------------------------------------------------------------------

    def kill(self, signal="", serviceTarget=""):
        '''From the launchctl man page:
          kill signal-name | signal-number service-target
              Sends the specified signal to the specified service if it is
              running. The signal number or name (SIGTERM, SIGKILL, etc.) may
              be specified.

        :param signal:  (Default value = "")
        :param serviceTarget:  (Default value = "")

        '''
        success = False
        args = []
        #####
        # Validate signal - from the signal(3) manpage on OS X.
        signals = [
            'SIGHUP', 'SIGINT', 'SIGQUIT', 'SIGILL', 'SIGTRAP', 'SIGABRT',
            'SIGEMT', 'SIGFPE', 'SIGKILL', 'SIGBUS', 'SIGSEGV', 'SIGSYS',
            'SIGPIPE', 'SIGALRM', 'SIGTERM', 'SIGURG', 'SIGSTOP', 'SIGTSTP',
            'SIGCONT', 'SIGCHLD', 'SIGTTIN', 'SIGTTOU', 'SIGIO', 'SIGXCPU',
            'SIGXFSZ', 'SIGVTALRM', 'SIGPROF', 'SIGWINCH', 'SIGINFO',
            'SIGUSR1', 'SIGUSR2'
        ]
        if isinstance(signal, str) and signal in signals:
            args.append(signal)
        elif isinstance(signal, int) and signal < 32:
            args.append(signal)
        else:
            return success

        #####
        # Service target, just check for string...
        if isinstance(serviceTarget, str):
            args.append(serviceTarget)
        else:
            return success

        args.append(serviceTarget)

        cmd = {"kill": args}
        success, _, _, _ = self.runSubCommand(cmd)

        return success

    # -------------------------------------------------------------------------

    def blame(self, serviceTarget):
        '''From the launchctl man page:
          blame service-target
              If the service is running, prints a human-readable string
              describing why launchd launched the service. Note that services
              may run for many reasons; this subcommand will only show the
              most proximate reason. So if a service was run due to a timer
              firing, this subcommand will print that reason, irrespective of
              whether there were messages waiting on the service's various
              endpoints. This subcommand is only intended for debugging and
              profiling use and its output should not be relied upon in pro-
              duction scenarios.

        :param serviceTarget: 

        '''
        success = False
        #####
        # Input validation.
        if not isinstance(serviceTarget, str):
            return success

        cmd = {"blame": [serviceTarget]}
        success, stdout, _, _ = self.runSubCommand(cmd)

        return success, stdout

    # -------------------------------------------------------------------------

    def printTarget(self, target):
        '''@note: From the launchctl man page:
          print domain-target | service-target
              Prints information about the specified service or domain.
              Domain output includes various properties about the domain as
              well as a list of services and endpoints in the domain with
              state pertaining to each. Service output includes various prop-
              erties of the service, including information about its origin
              on-disk, its current state, execution context, and last exit
              status.
        
              IMPORTANT: This output is NOT API in any sense at all. Do NOT
              rely on the structure or information emitted for ANY reason. It
              may change from release to release without warning.
        
        @author: Roy Nielsen

        :param target: 

        '''
        success = False
        #####
        # Input validation.
        if not isinstance(target, str):
            return success

        # prepended system/ to service-target in order to hot fix multiple
        # issues with service detection in servicehelper two implementation
        # all rules calling new servicehelper must specify the service target
        # context  and they all currently do not. system/ is where all
        # system services run. currently servicehelper two cannot look for
        # user context services when being run in admin mode anyway, so this
        # is just a best-effort workaround until servicehelper two can
        # be redesigned or all the rules changed to prepend system/ in
        # their servicehelper calls
        cmd = {"print": ["system/" + target]}
        success, stdout, stderr, _ = self.runSubCommand(cmd)

        if re.search("Could not find service", stderr) and \
           re.search("in domain for system", stderr):
            success = False

        return success, stdout

    # -------------------------------------------------------------------------

    def printCache(self):
        '''@note: From the launchctl man page:
        print-cache
              Prints the contents of the launchd service cache.
        
        @author: Roy Nielsen


        '''
        cmd = {"print-cache": []}
        success, stdout, _, _ = self.runSubCommand(cmd)
        if success:
            self.logger.log(lp.DEBUG, str(success))
            self.logger.log(lp.DEBUG, str(stdout))

        return success, stdout

    # -------------------------------------------------------------------------

    def printDisabled(self, target=''):
        '''@note: From the launchctl man page:
          print-disabled
              Prints the list of disabled services.
        
        @author: Roy Nielsen

        :param target:  (Default value = '')

        '''
        success = False
        stdout = ''
        if target and isinstance(target, str):
            cmd = {"print-disabled": [target]}
            success, stdout, _, _ = self.runSubCommand(cmd)

        return success, stdout

    # -------------------------------------------------------------------------

    def procInfo(self, pid):
        '''@note: From the launchctl man page:
          procinfo pid
              Prints information about the execution context of the specified
              PID. This information includes Mach task-special ports and

        :param pid: 
        :raises what: names the ports are advertised as in the Mach bootstrap
        :raises namespace: if they are known to launchd
        :raises text.: This subcommand is intended for diagnostic purposes only
        :raises and: its output should not be relied upon in production scenar
        :raises ios.: This command requires root privileges
        :raises author: Roy Nielsen

        '''
        success = False
        #####
        # Input validation.
        if not isinstance(pid, int):
            return success

        cmd = {"procinfo": [pid]}
        success, stdout, _, _ = self.runSubCommand(cmd)

        return success, stdout

    # -------------------------------------------------------------------------

    def hostinfo(self):
        '''@note: From the launchctl man page:
          hostinfo
              Prints information about the system's host-special ports,
              including the host-exception port. This subcommand requires
              root privileges.
        
        @author: Roy Nielsen


        '''
        cmd = {"hostinfo": []}
        _, stdout, _, _ = self.runSubCommand(cmd)

        return stdout

    # -------------------------------------------------------------------------

    def resolvePort(self, ownerPid, portName):
        '''@note: From the launchctl man page:
          resolveport owner-pid port-name
              Given a PID and the name of a Mach port right in that process'
              port namespace, resolves that port to an endpoint name known to
              launchd.  This subcommand requires root privileges.
        
        @author: Roy Nielsen

        :param ownerPid: 
        :param portName: 

        '''
        success = False
        #####
        # Input validation.
        if not isinstance(ownerPid, int) or not isinstance(portName, str):
            return success

        cmd = {"rsolveport": [ownerPid, portName]}
        _, stdout, _, _ = self.runSubCommand(cmd)

        return stdout

    # -------------------------------------------------------------------------

    def reboot(self, context, mountPoint):
        '''@note: From the launchctl man page:
          reboot [system|userspace|halt|logout|apps|reroot <mount-point>]
              Instructs launchd to begin tearing down userspace. With no
              argument given or with the system argument given, launchd will
              make the reboot(2) system call when userspace has been com-
              pletely torn down. With the halt argument given, launchd will
              make the reboot(2) system call when userspace has been com-
              pletely torn down and pass the RB_HALT flag, halting the system
              and not initiating a reboot.
        
              With the userspace argument given, launchd will re-exec itself
              when userspace has been torn down and bring userspace back up.
              This is useful for rebooting the system quickly under condi-
              tions where kernel data structures or hardware do not need to
              be re-initialized.
        
              With the reroot argument given, launchd will perform a
              userspace shutdown as with the userspace argument, but it will
              exec a copy of launchd from the specified mount-point.  This
              mechanism is a light-weight way of changing boot partitions. As
              part of this process, launchd will make mount-point the new
              root partition and bring userspace up as if the kernel had des-
              ignated mount-point as the root partition.
        
              IMPORTANT: This type of reboot will, in no way, affect the
              already-running kernel on the host. Therefore, when using this
              option to switch to another volume, you should only target vol-
              umes whose userspace stacks are compatible with the already-
              running kernel.
        
              NOTE: As of the date of this writing, this option does not com-
              pletely work.
        
              With the logout argument given, launchd will tear down the
              caller's GUI login session in a manner similar to a logout ini-
              tiated from the Apple menu. The key difference is that a logout
              initiated through this subcommand will be much faster since it
              will not give apps a chance to display modal dialogs to block
              logout indefinitely; therefore there is data corruption risk to
              using this option. Only use it when you know you have no
              unsaved data in your running apps.
        
              With the apps argument given, launchd will terminate all apps
              running in the caller's GUI login session that did not come
              from a launchd.plist(5) on-disk. Apps like Finder, Dock and
              SystemUIServer will be unaffected. Apps are terminated in the
              same manner as the logout argument, and all the same caveats
              apply.
        
              -s       When rebooting the machine (either a full reboot or
                       userspace reboot), brings the subsequent boot session
                       up in single-user mode.
        
        @author: Roy Nielsen

        :param context: 
        :param mountPoint: 

        '''
        success = False
        validContexts = ['System', 'users', 'halt', 'logout', 'apps', 'reroot']

        if not isinstance(context, str) or \
           not context in validContexts:
            return success
        if mountPoint and isinstance(mountPoint, str):
            cmd = {"reboot": [context, mountPoint]}
        elif not mountPoint:
            cmd = {"reboot": [context]}
        else:
            return success

        success, _, _, _ = self.runSubCommand(cmd)

        return success
Beispiel #11
0
class AptGet(object):
    """Linux specific package manager for distributions that use the apt-get
    command to install packages.
    
    @author: Derek T Walker
    @change: 2012/08/06 Derek Walker - Original Implementation
    @change: 2015/08/20 eball - Added getPackageFromFile
    @change: 2017/04/27 Breen Malmberg - added two methods checkUpdate
            and Update; fixed doc string formatting; removed detailedresults
            reset in init; replaced with --force-yes flag with --assume-yes
            (from the man page for apt-get: Force yes. This is a dangerous
            option that will cause apt-get to continue without prompting
            if it is doing something potentially harmful. It should not
            be used except in very special situations. Using --force-yes
            can potentially destroy your system!)
    @change: 2017/08/16 bgonz12 - Added DEBIAN_FRONTEND=noninteractive env var
            to remove function
    @change: 2017/10/18 Breen Malmberg - changed class var names to be more self-explanatory;
            changed command to check whether there are available packages to use the canonical
            debian/ubuntu method; added calls to repoError exception to determine exact nature
            and cause of any errors with querying or calling repositories on the system (this adds
            logging of the nature and cause(s) as well); changed log messaging to be more consistent
            in style/format; removed calls to validateParam due to concerns about the stability and
            reliability of that method


    """

    def __init__(self, logger):

        self.logger = logger
        self.ch = CommandHelper(self.logger)

        self.aptgetloc = "/usr/bin/apt-get"
        self.aptcacheloc = "/usr/bin/apt-cache"
        self.dpkgloc = "/usr/bin/dpkg"

        self.aptinstall = "DEBIAN_FRONTEND=noninteractive " + self.aptgetloc + " -y --assume-yes install "
        self.aptremove = "DEBIAN_FRONTEND=noninteractive " + self.aptgetloc + " -y remove "

        self.aptchkupdates = self.aptgetloc + " list --upgradeable "
        self.aptupgrade = self.aptgetloc + " -u upgrade --assume-yes "
        self.checkinstalled = "/usr/bin/apt list --installed "
        self.checkavailable = "/usr/bin/apt-cache search --names-only "
        self.findpkgforfilename = "/usr/bin/dpkg -S "
        self.pkgerrors = [1,100]

    def installpackage(self, package):
        """Install a package. Return a bool indicating success or failure.

        :param package: string; Name of the package to be installed, must be
                recognizable to the underlying package manager.
        :returns: installed
        :rtype: bool
@author: Derek Walker
@change: Breen Malmberg - 4/27/2017 - fixed doc string formatting;
        method now returns a variable; parameter validation added
        detailedresults replaced with logging
@change: Breen Malmberg - 10/1/2018 - added check for package manager lock and retry loop

        """

        installed = True
        maxtries = 12
        trynum = 0
        pslist = ["apt", "apt-get", "dpkg"]

        if type(package) is bytes:
            package = package.decode('utf-8')

        for ps in pslist:
            while psRunning(ps):
                trynum += 1
                if trynum == maxtries:
                    self.logger.log(LogPriority.DEBUG, "Timed out while attempting to install package, due to Apt package manager being in-use by another process.")
                    installed = False
                    return installed
                else:
                    self.logger.log(LogPriority.DEBUG, "Apt package manager is in-use by another process. Waiting for it to be freed...")
                    time.sleep(5)

        try:

            self.ch.executeCommand(self.aptinstall + package)
            retcode = self.ch.getReturnCode()
            errstr = self.ch.getErrorString()
            # recursive call to this method if package manager is still locked
            if re.search("Could not get lock", errstr, re.I):
                self.logger.log(LogPriority.DEBUG, "Apt package manager is in-use by another process. Waiting for it to be freed...")
                time.sleep(5)
                return self.installpackage(package)
            elif retcode in self.pkgerrors:
                installed = False
                self.logger.log(LogPriority.DEBUG, str(errstr))

            if installed:
                self.logger.log(LogPriority.DEBUG, "Successfully installed package " + str(package))
            else:
                self.logger.log(LogPriority.DEBUG, "Failed to install package " + str(package))

        except Exception:
            raise
        return installed

    def removepackage(self, package):
        """Remove a package. Return a bool indicating success or failure.

        :param package: string; Name of the package to be removed, must be
                recognizable to the underlying package manager.
        :returns: removed
        :rtype: bool
@author: Derek T. Walker

        """

        removed = True
        maxtries = 12
        trynum = 0
        pslist = ["apt", "apt-get", "dpkg"]

        if type(package) is bytes:
            package = package.decode('utf-8')

        for ps in pslist:
            while psRunning(ps):
                trynum += 1
                if trynum == maxtries:
                    self.logger.log(LogPriority.DEBUG, "Timed out while attempting to remove package, due to Apt package manager being in-use by another process.")
                    removed = False
                    return removed
                else:
                    self.logger.log(LogPriority.DEBUG, "Apt package manager is in-use by another process. Waiting for it to be freed...")
                    time.sleep(5)

        try:

            self.ch.executeCommand(self.aptremove + package)
            retcode = self.ch.getReturnCode()
            if retcode in self.pkgerrors:
                errstr = self.ch.getErrorString()
                removed = False
                self.logger.log(LogPriority.DEBUG, str(errstr))

            if removed:
                self.logger.log(LogPriority.DEBUG, "Successfully removed package " + str(package))
            else:
                self.logger.log(LogPriority.DEBUG, "Failed to remove package " + str(package))

        except Exception:
            raise
        return removed

    def checkInstall(self, package):
        """Check the installation status of a package. Return a bool; True if
        the package is installed.

        :param package: 
        :returns: installed
        :rtype: bool
@author: Derek Walker
@change: Breen Malmberg - 4/27/2017 - fixed doc string formatting;
        method now returns a variable; replaced detailedresults with
        logging

        """

        installed = False
        maxtries = 12
        trynum = 0
        pslist = ["apt", "apt-get", "dpkg"]

        if type(package) is bytes:
            package = package.decode('utf-8')

        for ps in pslist:
            while psRunning(ps):
                trynum += 1
                if trynum == maxtries:
                    self.logger.log(LogPriority.DEBUG, "Timed out while attempting to check status of package, due to Apt package manager being in-use by another process.")
                    installed = False
                    return installed
                else:
                    self.logger.log(LogPriority.DEBUG, "Apt package manager is in-use by another process. Waiting for it to be freed...")
                    time.sleep(5)

        try:

            self.ch.executeCommand(self.checkinstalled + package)
            retcode = self.ch.getReturnCode()
            outputstr = self.ch.getOutputString()
            if retcode in self.pkgerrors:
                errstr = self.ch.getErrorString()
                self.logger.log(LogPriority.DEBUG, str(errstr))

            if re.search(package + ".*installed", outputstr, re.I):
                installed = True

            if not installed:
                self.logger.log(LogPriority.DEBUG, "Package " + str(package) + " is NOT installed")
            else:
                self.logger.log(LogPriority.DEBUG, "Package " + str(package) + " is installed")

        except Exception:
            raise
        return installed

    def checkAvailable(self, package):
        """check if a given package is available

        :param package: string; Name of package to check
        :returns: found
        :rtype: bool
@author: Derek T. Walker
@change: Breen Malmberg - 4/27/2017 - created doc string;
        pulled result logging out of conditional

        """

        found = False
        outputstr = ""
        maxtries = 12
        trynum = 0
        pslist = ["apt", "apt-get", "dpkg"]

        if type(package) is bytes:
            package = package.decode('utf-8')

        for ps in pslist:
            while psRunning(ps):
                trynum += 1
                if trynum == maxtries:
                    self.logger.log(LogPriority.DEBUG,
                                    "Timed out while attempting to check availability of package, due to apt package manager being in-use by another process.")
                    available = False
                    return available
                else:
                    self.logger.log(LogPriority.DEBUG,
                                    "apt package manager is in-use by another process. Waiting for it to be freed...")
                    time.sleep(5)

        try:

            self.ch.executeCommand(self.checkavailable + "^" + package + "$")
            retcode = self.ch.getReturnCode()
            if retcode in self.pkgerrors:
                errstr = self.ch.getErrorString()
                self.logger.log(LogPriority.DEBUG, errstr)
            else:
                outputstr = self.ch.getOutputString()
                if re.search("^" + package, outputstr, re.I):
                    found = True

            if found:
                self.logger.log(LogPriority.DEBUG, "Package " + str(package) + " is available to install")
            else:
                self.logger.log(LogPriority.DEBUG, "Package " + str(package) + " is NOT available to install")

        except Exception:
            raise
        return found

    def Update(self, package=""):
        """update the specified package if any
        updates are available for it
        if no package is specified, apply
        all available updates for the system

        :param package: string; (OPTIONAL) name of package to update (Default value = "")
        :returns: updated
        :rtype: bool
@author: Breen Malmberg

        """

        updated = True

        try:

            if type(package) is bytes:
                package = package.decode('utf-8')

            self.ch.executeCommand(self.aptupgrade + package)
            retcode = self.ch.getReturnCode()
            if retcode in self.pkgerrors:
                errstr = self.ch.getErrorString()
                updated = False
                self.logger.log(LogPriority.DEBUG, errstr)

            if package:
                if updated:
                    self.logger.log(LogPriority.DEBUG, "Package " + str(package) + " was updated successfully")
                else:
                    self.logger.log(LogPriority.DEBUG, "Failed to apply updates to package " + str(package))
            else:
                if updated:
                    self.logger.log(LogPriority.DEBUG, "All updates were installed successfully")
                else:
                    self.logger.log(LogPriority.DEBUG, "Failed to apply updates")

        except Exception:
            raise
        return updated

    def checkUpdate(self, package=""):
        """check for updates for specified package
        if no package is specified, then check
        for updates for the entire system

        :param package: string; (OPTIONAL) Name of package to check (Default value = "")
        :returns: updatesavail
        :rtype: bool
@author: Breen Malmberg

        """

        updatesavail = False

        try:

            self.ch.executeCommand(self.aptchkupdates + package)
            retcode = self.ch.getReturnCode()
            if retcode in self.pkgerrors:
                errstr = self.ch.getErrorString()
                self.logger.log(LogPriority.DEBUG, errstr)
            else:
                outputstr = self.ch.getOutputString()
                if re.search("upgradable", outputstr, re.I):
                    updatesavail = True

            if package:
                if updatesavail:
                    self.logger.log(LogPriority.DEBUG, "Updates are available for package " + str(package))
                else:
                    self.logger.log(LogPriority.DEBUG, "No updates are available for package " + str(package))
            else:
                if updatesavail:
                    self.logger.log(LogPriority.DEBUG, "Updates are available for this system")
                else:
                    self.logger.log(LogPriority.DEBUG, "No updates are available for this system")

        except Exception:
            raise
        return updatesavail

    def getPackageFromFile(self, filename):
        """Returns the name of the package that provides the given
        filename/path.

        :param filename: 
        :returns: packagename
        :rtype: string
@author: Eric Ball
@change: Breen Malmberg - 4/17/2017 - fixed doc string formatting;
        method now returns a variable; added param validation

        """

        packagename = ""

        try:

            try:
                self.ch.executeCommand(self.findpkgforfilename + filename)
                retcode = self.ch.getReturnCode()
                errstr = self.ch.getErrorString()
                if retcode != 0:
                    raise repoError('apt', retcode, str(errstr))
                if self.ch.getReturnCode() == 0:
                    output = self.ch.getOutputString()
                    packagename = output.split(":")[0]
            except repoError as repoerr:
                if not repoerr.success:
                    self.logger.log(LogPriority.WARNING, str(errstr))
                    pass

        except Exception:
            raise
        return packagename

    def getInstall(self):
        return self.aptinstall

    def getRemove(self):
        return self.aptremove
Beispiel #12
0
class SHupdaterc(ServiceHelperTemplate):
    """SHupdaterc is the Service Helper for systems using the rcupdate command to
    configure services. (Debian, Ubuntu and variants)


    """
    def __init__(self, environment, logdispatcher):
        """
        Constructor
        """
        super(SHupdaterc, self).__init__(environment, logdispatcher)
        self.environment = environment
        self.logdispatcher = logdispatcher
        self.ch = CommandHelper(self.logdispatcher)
        self.updaterc = "/usr/sbin/update-rc.d "
        self.svc = "/usr/sbin/service "

    def disableService(self, service, **kwargs):
        """Disables the service and terminates it if it is running.

        :param service: string; name of service
        :param kwargs: dict; dictionary of key-value arguments
        :param **kwargs: 
        :returns: enabled
        :rtype: bool
@author: ???

        """

        disabled = True

        self.logdispatcher.log(LogPriority.DEBUG,
                               "Disabling service: " + service)

        self.ch.executeCommand(self.updaterc + service + " disable")
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            errmsg = self.ch.getErrorString()
            self.logdispatcher.log(LogPriority.DEBUG, errmsg)
            disabled = False
        else:
            if self.auditService(service):
                disabled = False

        if disabled:
            self.logdispatcher.log(LogPriority.DEBUG,
                                   "Successfully disabled service: " + service)

        return disabled

    def enableService(self, service, **kwargs):
        """Enables a service and starts it if it is not running as long as we are
        not in install mode

        :param service: string; name of service
        :param kwargs: dict; dictionary of key-value arguments
        :param **kwargs: 
        :returns: enabled
        :rtype: bool
@author: ???

        """

        enabled = True

        self.logdispatcher.log(LogPriority.DEBUG,
                               "Enabling service: " + service)

        self.ch.executeCommand(self.updaterc + service + " enable")
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            errmsg = self.ch.getErrorString()
            self.logdispatcher.log(LogPriority.DEBUG, errmsg)
            enabled = False
        else:
            if not self.auditService(service):
                enabled = False

        if enabled:
            self.logdispatcher.log(LogPriority.DEBUG,
                                   "Successfully enabled service: " + service)

        return enabled

    def auditService(self, service, **kwargs):
        """Checks all /etc/rc*.d/ directories for the "S" (start) service entry
        in updaterc, if an "S" entry with the service name exists in any of the rc*.d/
        directories, it means that the service is scheduled to start at boot

        :param service: string; name of service
        :param kwargs: dict; dictionary of key-value arguments
        :param **kwargs: 
        :returns: enabled
        :rtype: bool
@author: ???

        """

        enabled = False

        self.logdispatcher.log(
            LogPriority.DEBUG,
            "Checking if service: " + service + " is enabled")

        self.ch.executeCommand("ls -l /etc/rc*.d/")
        if self.ch.findInOutput("S[0-9]+" + service):
            enabled = True

        if enabled:
            self.logdispatcher.log(LogPriority.DEBUG,
                                   "Service: " + service + " is enabled")
        else:
            self.logdispatcher.log(LogPriority.DEBUG,
                                   "Service: " + service + " is disabled")

        return enabled

    def isRunning(self, service, **kwargs):
        """Check to see if a service is currently running.

        :param service: string; name of service
        :param kwargs: dict; dictionary of key-value arguments
        :param **kwargs: 
        :returns: enabled
        :rtype: bool
@author: ???

        """

        running = False

        self.logdispatcher.log(
            LogPriority.DEBUG,
            "Checking if service: " + service + " is running")

        self.ch.executeCommand(self.svc + "--status-all")
        if self.ch.findInOutput("\[\s+\+\s+\]\s+" + service):
            running = True

        if running:
            self.logdispatcher.log(LogPriority.DEBUG,
                                   "Service: " + service + " IS running")
        else:
            self.logdispatcher.log(LogPriority.DEBUG,
                                   "Service: " + service + " is NOT running")

        return running

    def reloadService(self, service, **kwargs):
        """Reload (HUP) a service so that it re-reads it's config files. Called
        by rules that are configuring a service to make the new configuration
        active.

        :param service: string; name of service
        :param kwargs: dict; dictionary of key-value arguments
        :param **kwargs: 
        :returns: enabled
        :rtype: bool
@author: ???

        """

        reloaded = True

        self.logdispatcher.log(LogPriority.DEBUG,
                               "Reloading service: " + service)

        self.ch.executeCommand(self.svc + service + " stop")
        self.ch.executeCommand(self.svc + service + " start")
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            reloaded = False
            errmsg = self.ch.getErrorString()
            self.logdispatcher.log(LogPriority.DEBUG, errmsg)
        else:
            if not self.isRunning(service):
                reloaded = False
                self.logdispatcher.log(LogPriority.DEBUG,
                                       "Failed to reload service: " + service)

        return reloaded

    def listServices(self, **kwargs):
        """Return a list containing strings that are service names.

        :param kwargs: dict; dictionary of key-value arguments
        :param **kwargs: 
        :returns: enabled
        :rtype: bool
@author: ???

        """

        services = []

        self.logdispatcher.log(LogPriority.DEBUG, "Fetching list of services")

        self.ch.executeCommand(self.svc + "--status-all")
        output = self.ch.getOutput()
        for line in output:
            if re.search("^\[", line):
                try:
                    services.append(line.split("]")[1].strip())
                except (IndexError, AttributeError):
                    continue

        return services

    def getStartCommand(self, service):
        """retrieve the start command.  Mostly used by event recording

        :param service: 
        :returns: string - start command
        @author: Derek Walker

        """
        return self.svc + service + ' start'

    def getStopCommand(self, service):
        """retrieve the stop command.  Mostly used by event recording

        :param service: 
        :returns: string - stop command
        @author: Derek Walker

        """
        return self.svc + service + ' stop'

    def getEnableCommand(self, service):
        """retrieve the enable command.  Mostly used by event recording

        :param service: 
        :returns: string - enable command
        @author: Derek Walker

        """
        return self.updaterc + service + ' enable'

    def getDisableCommand(self, service):
        """retrieve the start command.  Mostly used by event recording

        :param service: 
        :returns: string - disable command
        @author: Derek Walker

        """
        return self.updaterc + service + ' disable'
Beispiel #13
0
class Dnf(object):
    '''The template class that provides a framework that must be implemented by
    all platform specific pkgmgr classes.  Specifically for Fedora
    
    :version:
    @author: Derek T Walker 08-13-2015
    @change: Breen Malmberg - 4/18/2017 - refactor of multiple methods;
            removed detailedresults reset in __init__; added the -q
            (non-interactive) flag to install and remove command var's;
            added a dnf info command var;
            added parameter validation to each method


    '''
    def __init__(self, logger):
        self.logger = logger
        self.ch = CommandHelper(self.logger)
        self.dnfloc = "/usr/bin/dnf"
        self.install = self.dnfloc + " install -yq "
        self.remove = self.dnfloc + " remove -yq "
        self.search = self.dnfloc + " search "
        self.checkinstalled = self.dnfloc + " list --installed "
        self.chavailable = self.dnfloc + " list --available "
        self.checkupdate = self.dnfloc + " check-update "
        self.rpm = "/bin/rpm -qf "
        self.updatepackage = self.dnfloc + " -yq upgrade "
        self.lockfiles = [
            "/var/run/dnf.lock", "/var/run/dnf.pid", "/run/dnf.lock",
            "/run/dnf.pid"
        ]

    def installpackage(self, package):
        '''Install a package. Return a bool indicating success or failure.

        :param package: string; Name of the package to be installed, must be
                recognizable to the underlying package manager.
        :returns: installed
        :rtype: bool
@author: Derek T. Walker
@change: Breen Malmberg - 4/18/2017 - refactored method; added logging; replaced
        detailedresults with logging
@change: Breen Malmberg - 10/1/2018 - added check for package manager lock and retry loop

        '''

        installed = True
        maxtries = 12
        trynum = 0

        while psRunning("dnf"):
            trynum += 1
            if trynum == maxtries:
                self.logger.log(
                    LogPriority.DEBUG,
                    "Timed out while attempting to install package, due to dnf package manager being in-use by another process."
                )
                installed = False
                return installed
            else:
                self.logger.log(
                    LogPriority.DEBUG,
                    "dnf package manager is in-use by another process. Waiting for it to be freed..."
                )
                time.sleep(5)

        try:

            try:
                self.ch.executeCommand(self.install + package)
                retcode = self.ch.getReturnCode()
                if retcode != 0:
                    raise repoError('dnf', retcode)
            except repoError as repoerr:
                if not repoerr.success:
                    installed = False

            if installed:
                self.logger.log(
                    LogPriority.DEBUG,
                    "Successfully installed package " + str(package))
            else:
                self.logger.log(LogPriority.DEBUG,
                                "Failed to install package " + str(package))

        except Exception:
            raise
        return installed

    def removepackage(self, package):
        '''Remove a package. Return a bool indicating success or failure.

        :param package: string; Name of the package to be removed, must be
                recognizable to the underlying package manager.
        :returns: removed
        :rtype: bool
@author: Derek T. Walker
@change: Breen Malmberg - 4/18/2017

        '''

        removed = True
        maxtries = 12
        trynum = 0

        while psRunning("dnf"):
            trynum += 1
            if trynum == maxtries:
                self.logger.log(
                    LogPriority.DEBUG,
                    "Timed out while attempting to remove package, due to dnf package manager being in-use by another process."
                )
                installed = False
                return installed
            else:
                self.logger.log(
                    LogPriority.DEBUG,
                    "dnf package manager is in-use by another process. Waiting for it to be freed..."
                )
                time.sleep(5)

        try:

            try:
                self.ch.executeCommand(self.remove + package)
                retcode = self.ch.getReturnCode()
                if retcode != 0:
                    raise repoError('dnf', retcode)
            except repoError as repoerr:
                if not repoerr.success:
                    removed = False

            if removed:
                self.logger.log(
                    LogPriority.DEBUG,
                    "Package " + str(package) + " was successfully removed")
            else:
                self.logger.log(LogPriority.DEBUG,
                                "Failed to remove package " + str(package))

        except Exception:
            raise
        return removed

    def checkInstall(self, package):
        '''Check the installation status of a package. Return a bool; True if
        the package is installed.

        :param package: 
        :returns: installed
        :rtype: bool
@author: Derek T. Walker
@change: Breen Malmberg - 4/18/2017

        '''

        installed = False
        errstr = ""
        outputstr = ""
        maxtries = 12
        trynum = 0

        while psRunning("dnf"):
            trynum += 1
            if trynum == maxtries:
                self.logger.log(
                    LogPriority.DEBUG,
                    "Timed out while attempting to check status of package, due to dnf package manager being in-use by another process."
                )
                installed = False
                return installed
            else:
                self.logger.log(
                    LogPriority.DEBUG,
                    "dnf package manager is in-use by another process. Waiting for it to be freed..."
                )
                time.sleep(5)

        try:

            try:
                # There is no dnf search command which will only return an
                # "installed" result set. Therefore we must parse the output
                # to determine if the package is installed or just available.
                # The below command string will produce stdout with only the
                # installed result set of packages
                self.ch.executeCommand(self.checkinstalled + package +
                                       " | grep -iA 1 installed")
                retcode = self.ch.getReturnCode()
                errstr = self.ch.getErrorString()
                outputstr = self.ch.getOutputString()
                # With this command specifically, in this package manager, we
                # can't count exit code 1 as being an error because the check installed
                # command (with dnf) will return an error (1) exit code if no results are
                # returned, even if there is no error. We also can't use error or output strings
                # to parse because it is possible for this command to also return no output of any
                # kind, in addition to returning a 1 exit code... Therefore we must exempt exit
                # code 1 for this command specifically...
                if retcode != 0 | 1:
                    raise repoError('dnf', retcode, str(errstr))
                else:
                    if re.search(package, outputstr, re.IGNORECASE):
                        installed = True
            except repoError as repoerr:
                if not repoerr.success:
                    self.logger.log(LogPriority.WARNING, str(errstr))
                    installed = False
                else:
                    if re.search(package, outputstr, re.IGNORECASE):
                        installed = True

            if installed:
                self.logger.log(LogPriority.DEBUG,
                                "Package " + str(package) + " is installed")
            else:
                self.logger.log(
                    LogPriority.DEBUG,
                    "Package " + str(package) + " is NOT installed")

        except Exception:
            raise
        return installed

    def checkAvailable(self, package):
        '''check if the given package is available to install

        :param package: string; name of package to check for
        :returns: found
        :rtype: bool
@author: Derek T. Walker
@change: Breen Malmberg - 4/18/2017 - added doc string; refactor of method

        '''

        found = False
        maxtries = 12
        trynum = 0

        while psRunning("dnf"):
            trynum += 1
            if trynum == maxtries:
                self.logger.log(
                    LogPriority.DEBUG,
                    "Timed out while attempting to check availability of package, due to dnf package manager being in-use by another process."
                )
                found = False
                return found
            else:
                self.logger.log(
                    LogPriority.DEBUG,
                    "dnf package manager is in-use by another process. Waiting for it to be freed..."
                )
                time.sleep(5)

        try:

            try:
                self.ch.executeCommand(self.chavailable + package)
                retcode = self.ch.getReturnCode()
                outputstr = self.ch.getOutputString()
                errstr = self.ch.getErrorString()
                if retcode != 0:
                    raise repoError('dnf', retcode, str(errstr))
                else:
                    if re.search(package, outputstr, re.IGNORECASE):
                        found = True
            except repoError as repoerr:
                if not repoerr.success:
                    self.logger.log(LogPriority.WARNING, str(errstr))
                else:
                    if re.search(package, outputstr, re.IGNORECASE):
                        found = True

            if found:
                self.logger.log(
                    LogPriority.DEBUG,
                    "Package " + str(package) + " is available to install")
            else:
                self.logger.log(
                    LogPriority.DEBUG,
                    "No package " + str(package) + " available to install")

        except Exception:
            raise
        return found

    def checkUpdate(self, package=""):
        '''check the specified package for updates
        if no package is specified, check for all/any
        updates on the system
        return True if there are updates available
        return False if there are no updates available

        :param package: string; name of package to check (Default value = "")
        :returns: updatesavail
        :rtype: bool
@author: Breen Malmberg

        '''

        updatesavail = False

        try:

            try:
                self.ch.executeCommand(self.checkupdate + package)
                retcode = self.ch.getReturnCode()
                errstr = self.ch.getErrorString()
                if retcode != 0:
                    raise repoError('dnf', retcode)
            except repoError as repoerr:
                if not repoerr.success:
                    self.logger.log(LogPriority.WARNING, str(errstr))
                    return False
                else:
                    updatesavail = True

            if package:
                if updatesavail:
                    self.logger.log(LogPriority.DEBUG, "Updates are available")
                else:
                    self.logger.log(LogPriority.DEBUG,
                                    "No updates are available")
            else:
                if updatesavail:
                    self.logger.log(
                        LogPriority.DEBUG,
                        "Updates are available for package " + str(package))
                else:
                    self.logger.log(
                        LogPriority.DEBUG,
                        "No updates are available for package " + str(package))

        except Exception:
            raise
        return updatesavail

    def Update(self, package=""):
        '''update the specified package
        if no package is specified, update
        all packages on the system

        :param package: string; name of package to update (Default value = "")
        :returns: updated
        :rtype: bool
@author: Breen Malmberg

        '''

        updated = True

        try:

            try:
                self.ch.executeCommand(self.updatepackage + package)
                retcode = self.ch.getReturnCode()
                errstr = self.ch.getErrorString()
                if retcode != 0:
                    raise repoError('dnf', retcode)
                else:
                    updated = True
            except repoError as repoerr:
                if not repoerr.success:
                    self.logger.log(LogPriority.WARNING, str(errstr))
                    return False
                else:
                    updated = True

            if package:
                if updated:
                    self.logger.log(
                        LogPriority.DEBUG,
                        "Package " + str(package) + " successfully updated")
                else:
                    self.logger.log(
                        LogPriority.DEBUG,
                        "Package " + str(package) + " was NOT updated")
            else:
                if updated:
                    self.logger.log(LogPriority.DEBUG,
                                    "All packages updated successfully")
                else:
                    self.logger.log(
                        LogPriority.DEBUG,
                        "One or more packages failed to update properly")

        except Exception:
            raise
        return updated

    def getPackageFromFile(self, filename):
        '''return a string with the name of the parent package
        in it

        :param filename: 
        :returns: packagename
        :rtype: string
@author: Eric Ball
@change: Breen Malmberg - 4/18/2017 - fixed doc string; refactored method

        '''

        packagename = ""

        try:

            try:
                self.ch.executeCommand(self.rpm + filename)
                retcode = self.ch.getReturnCode()
                errstr = self.ch.getErrorString()
                outputstr = self.ch.getOutputString()
                if retcode != 0:
                    raise repoError('dnf', retcode, str(errstr))
                else:
                    packagename = outputstr
            except repoError as repoerr:
                if not repoerr.success:
                    self.logger.log(LogPriority.WARNING, str(errstr))
                else:
                    packagename = outputstr

        except Exception:
            raise
        return packagename

    def getInstall(self):
        return self.install

    def getRemove(self):
        return self.remove

    def getSearch(self):
        return self.search

    def getInfo(self):
        return self.info

    def getCheck(self):
        return self.checkupdate