Example #1
0
    def run_probes(self):
        """ run probes and upload probe data """
        try:
            probes = XML.XML(str(self.proxy.GetProbes()))
        except (Proxy.ProxyError,
                Proxy.CertificateError,
                socket.gaierror,
                socket.error):
            err = sys.exc_info()[1]
            self.fatal_error("Failed to download probes from bcfg2: %s" % err)
        except XML.ParseError:
            err = sys.exc_info()[1]
            self.fatal_error("Server returned invalid probe requests: %s" %
                             err)

        self.times['probe_download'] = time.time()

        # execute probes
        probedata = XML.Element("ProbeData")
        for probe in probes.findall(".//probe"):
            probedata.append(self.run_probe(probe))

        if len(probes.findall(".//probe")) > 0:
            try:
                # upload probe responses
                self.proxy.RecvProbeData(
                    XML.tostring(probedata,
                                 xml_declaration=False).decode('utf-8'))
            except Proxy.ProxyError:
                err = sys.exc_info()[1]
                self.fatal_error("Failed to upload probe data: %s" % err)

        self.times['probe_upload'] = time.time()
Example #2
0
    def GenerateStats(self):
        """Generate XML summary of execution statistics."""
        feedback = XML.Element("upload-statistics")
        stats = XML.SubElement(feedback,
                               'Statistics', total=str(len(self.states)),
                               version='2.0',
                               revision=self.config.get('revision', '-1'))
        good_entries = [key for key, val in list(self.states.items()) if val]
        good = len(good_entries)
        stats.set('good', str(good))
        if any(not val for val in list(self.states.values())):
            stats.set('state', 'dirty')
        else:
            stats.set('state', 'clean')

        # List bad elements of the configuration
        for (data, ename) in [(self.modified, 'Modified'),
                              (self.extra, "Extra"),
                              (good_entries, "Good"),
                              ([entry for entry in self.states
                                if not self.states[entry]], "Bad")]:
            container = XML.SubElement(stats, ename)
            for item in data:
                item.set('qtext', '')
                container.append(item)
                item.text = None

        timeinfo = XML.Element("OpStamps")
        feedback.append(stats)
        for (event, timestamp) in list(self.times.items()):
            timeinfo.set(event, str(timestamp))
        stats.append(timeinfo)
        return feedback
Example #3
0
    def run_probes(self):
        """ run probes and upload probe data """
        try:
            probes = XML.XML(str(self.proxy.GetProbes()))
        except (Proxy.ProxyError,
                Proxy.CertificateError,
                socket.gaierror,
                socket.error):
            err = sys.exc_info()[1]
            self.fatal_error("Failed to download probes from bcfg2: %s" % err)
        except XML.ParseError:
            err = sys.exc_info()[1]
            self.fatal_error("Server returned invalid probe requests: %s" %
                             err)

        self.times['probe_download'] = time.time()

        # execute probes
        probedata = XML.Element("ProbeData")
        for probe in probes.findall(".//probe"):
            probedata.append(self.run_probe(probe))

        if len(probes.findall(".//probe")) > 0:
            try:
                # upload probe responses
                self.proxy.RecvProbeData(
                    XML.tostring(probedata,
                                 xml_declaration=False).decode('utf-8'))
            except Proxy.ProxyError:
                err = sys.exc_info()[1]
                self.fatal_error("Failed to upload probe data: %s" % err)

        self.times['probe_upload'] = time.time()
Example #4
0
    def GenerateStats(self):
        """Generate XML summary of execution statistics."""
        states = {}
        for (item, val) in list(self.states.items()):
            if (not Bcfg2.Options.setup.only_important
                    or item.get('important', 'false').lower() == 'true'):
                states[item] = val

        feedback = XML.Element("upload-statistics")
        stats = XML.SubElement(feedback,
                               'Statistics',
                               total=str(len(states)),
                               version='2.0',
                               revision=self.config.get('revision', '-1'))
        flags = XML.SubElement(stats, "Flags")
        XML.SubElement(flags,
                       "Flag",
                       name="dry_run",
                       value=str(Bcfg2.Options.setup.dry_run))
        XML.SubElement(flags,
                       "Flag",
                       name="only_important",
                       value=str(Bcfg2.Options.setup.only_important))
        good_entries = [key for key, val in list(states.items()) if val]
        good = len(good_entries)
        stats.set('good', str(good))
        if any(not val for val in list(states.values())):
            stats.set('state', 'dirty')
        else:
            stats.set('state', 'clean')

        # List bad elements of the configuration
        for (data, ename) in [
            (self.modified, 'Modified'), (self.extra, "Extra"),
            (good_entries, "Good"),
            ([entry for entry in states if not states[entry]], "Bad")
        ]:
            container = XML.SubElement(stats, ename)
            for item in data:
                new_item = copy.deepcopy(item)
                new_item.set('qtext', '')
                container.append(new_item)
                new_item.text = None

        timeinfo = XML.Element("OpStamps")
        feedback.append(stats)
        for (event, timestamp) in list(self.times.items()):
            timeinfo.set(event, str(timestamp))
        stats.append(timeinfo)
        return feedback
Example #5
0
 def run_probe(self, probe):
     """Execute probe."""
     name = probe.get('name')
     self.logger.info("Running probe %s" % name)
     ret = XML.Element("probe-data", name=name, source=probe.get('source'))
     try:
         scripthandle, scriptname = tempfile.mkstemp()
         if sys.hexversion >= 0x03000000:
             script = os.fdopen(scripthandle, 'w',
                                encoding=Bcfg2.Options.setup.encoding)
         else:
             script = os.fdopen(scripthandle, 'w')
         try:
             script.write("#!%s\n" %
                          (probe.attrib.get('interpreter', '/bin/sh')))
             if sys.hexversion >= 0x03000000:
                 script.write(probe.text)
             else:
                 script.write(probe.text.encode('utf-8'))
             script.close()
             os.chmod(scriptname,
                      stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH |
                      stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH |
                      stat.S_IWUSR)  # 0755
             rv = self.cmd.run(scriptname)
             if rv.stderr:
                 self.logger.warning("Probe %s has error output: %s" %
                                     (name, rv.stderr))
             if not rv.success:
                 self._probe_failure(name, "Return value %s" % rv.retval)
             self.logger.info("Probe %s has result:" % name)
             self.logger.info(rv.stdout)
             if sys.hexversion >= 0x03000000:
                 ret.text = rv.stdout
             else:
                 ret.text = rv.stdout.decode('utf-8')
         finally:
             os.unlink(scriptname)
     except SystemExit:
         raise
     except:
         self._probe_failure(name, sys.exc_info()[1])
     return ret
Example #6
0
    def parse_config(self, rawconfig):
        """ Parse the XML configuration received from the Bcfg2 server """
        try:
            self.config = XML.XML(rawconfig)
        except XML.ParseError:
            syntax_error = sys.exc_info()[1]
            self.fatal_error("The configuration could not be parsed: %s" %
                             syntax_error)

        self.load_tools()

        # find entries not handled by any tools
        self.unhandled = [
            entry for struct in self.config for entry in struct
            if entry not in self.handled
        ]

        if self.unhandled:
            self.logger.error("The following entries are not handled by any "
                              "tool:")
            for entry in self.unhandled:
                self.logger.error(
                    "%s:%s:%s" %
                    (entry.tag, entry.get('type'), entry.get('name')))

        # find duplicates
        self.find_dups(self.config)

        pkgs = [(entry.get('name'), entry.get('origin'))
                for struct in self.config for entry in struct
                if entry.tag == 'Package']
        if pkgs:
            self.logger.debug("The following packages are specified in bcfg2:")
            self.logger.debug([pkg[0] for pkg in pkgs if pkg[1] is None])
            self.logger.debug("The following packages are prereqs added by "
                              "Packages:")
            self.logger.debug([pkg[0] for pkg in pkgs if pkg[1] == 'Packages'])

        self.times['config_parse'] = time.time()
Example #7
0
    def run(self):
        """Perform client execution phase."""
        # begin configuration
        self.times['start'] = time.time()

        self.logger.info("Starting Bcfg2 client run at %s" %
                         self.times['start'])

        self.parse_config(self.get_config().decode('utf-8'))

        if self.config.tag == 'error':
            self.fatal_error("Server error: %s" % (self.config.text))

        if Bcfg2.Options.setup.bundle_quick:
            newconfig = XML.XML('<Configuration/>')
            for bundle in self.config.getchildren():
                name = bundle.get("name")
                if (name and (name in Bcfg2.Options.setup.only_bundles or
                              name not in Bcfg2.Options.setup.except_bundles)):
                    newconfig.append(bundle)
            self.config = newconfig

        if not Bcfg2.Options.setup.no_lock:
            # check lock here
            try:
                lockfile = open(Bcfg2.Options.setup.lockfile, 'w')
                if locked(lockfile.fileno()):
                    self.fatal_error("Another instance of Bcfg2 is running. "
                                     "If you want to bypass the check, run "
                                     "with the -O/--no-lock option")
            except SystemExit:
                raise
            except:
                lockfile = None
                self.logger.error("Failed to open lockfile %s: %s" %
                                  (Bcfg2.Options.setup.lockfile,
                                   sys.exc_info()[1]))

        # execute the configuration
        self.Execute()

        if not Bcfg2.Options.setup.no_lock:
            # unlock here
            if lockfile:
                try:
                    fcntl.lockf(lockfile.fileno(), fcntl.LOCK_UN)
                    os.remove(Bcfg2.Options.setup.lockfile)
                except OSError:
                    self.logger.error("Failed to unlock lockfile %s" %
                                      lockfile.name)

        if (not Bcfg2.Options.setup.file and
                not Bcfg2.Options.setup.bundle_quick):
            # upload statistics
            feedback = self.GenerateStats()

            try:
                self.proxy.RecvStats(
                    XML.tostring(feedback,
                                 xml_declaration=False).decode('utf-8'))
            except Proxy.ProxyError:
                err = sys.exc_info()[1]
                self.logger.error("Failed to upload configuration statistics: "
                                  "%s" % err)
                raise SystemExit(2)

        self.logger.info("Finished Bcfg2 client run at %s" % time.time())
Example #8
0
    def run(self):
        """Perform client execution phase."""
        # begin configuration
        self.times['start'] = time.time()

        self.logger.info("Starting Bcfg2 client run at %s" %
                         self.times['start'])

        self.parse_config(self.get_config().decode('utf-8'))

        if self.config.tag == 'error':
            self.fatal_error("Server error: %s" % (self.config.text))

        if Bcfg2.Options.setup.bundle_quick:
            newconfig = XML.XML('<Configuration/>')
            for bundle in self.config.getchildren():
                name = bundle.get("name")
                if (name and (name in Bcfg2.Options.setup.only_bundles or
                              name not in Bcfg2.Options.setup.except_bundles)):
                    newconfig.append(bundle)
            self.config = newconfig

        if not Bcfg2.Options.setup.no_lock:
            # check lock here
            try:
                lockfile = open(Bcfg2.Options.setup.lockfile, 'w')
                if locked(lockfile.fileno()):
                    self.fatal_error("Another instance of Bcfg2 is running. "
                                     "If you want to bypass the check, run "
                                     "with the -O/--no-lock option")
            except SystemExit:
                raise
            except:
                lockfile = None
                self.logger.error("Failed to open lockfile %s: %s" %
                                  (Bcfg2.Options.setup.lockfile,
                                   sys.exc_info()[1]))

        # execute the configuration
        self.Execute()

        if not Bcfg2.Options.setup.no_lock:
            # unlock here
            if lockfile:
                try:
                    fcntl.lockf(lockfile.fileno(), fcntl.LOCK_UN)
                    os.remove(Bcfg2.Options.setup.lockfile)
                except OSError:
                    self.logger.error("Failed to unlock lockfile %s" %
                                      lockfile.name)

        if (not Bcfg2.Options.setup.file and
                not Bcfg2.Options.setup.bundle_quick):
            # upload statistics
            feedback = self.GenerateStats()

            try:
                self.proxy.RecvStats(
                    XML.tostring(feedback,
                                 xml_declaration=False).decode('utf-8'))
            except Proxy.ProxyError:
                err = sys.exc_info()[1]
                self.logger.error("Failed to upload configuration statistics: "
                                  "%s" % err)
                raise SystemExit(2)

        self.logger.info("Finished Bcfg2 client run at %s" % time.time())
Example #9
0
    def Verifydirectory(self, entry, modlist):
        """Verify Path type='directory' entry."""
        if entry.get('perms') == None or \
           entry.get('owner') == None or \
           entry.get('group') == None:
            self.logger.error("POSIX: Entry %s not completely specified. "
                              "Try running bcfg2-lint." % (entry.get('name')))
            return False
        while len(entry.get('perms', '')) < 4:
            entry.set('perms', '0' + entry.get('perms', ''))
        try:
            ondisk = os.stat(entry.get('name'))
        except OSError:
            entry.set('current_exists', 'false')
            self.logger.info("POSIX: %s %s does not exist" %
                             (entry.tag, entry.get('name')))
            return False
        try:
            owner = str(ondisk[stat.ST_UID])
            group = str(ondisk[stat.ST_GID])
        except (OSError, KeyError):
            self.logger.info("POSIX: User/Group resolution failed "
                             "for path %s" % entry.get('name'))
            owner = 'root'
            group = '0'
        finfo = os.stat(entry.get('name'))
        perms = oct(finfo[stat.ST_MODE])[-4:]
        if entry.get('mtime', '-1') != '-1':
            mtime = str(finfo[stat.ST_MTIME])
        else:
            mtime = '-1'
        pTrue = ((owner == str(normUid(entry)))
                 and (group == str(normGid(entry)))
                 and (perms == entry.get('perms'))
                 and (mtime == entry.get('mtime', '-1')))

        pruneTrue = True
        ex_ents = []
        if entry.get('prune', 'false') == 'true' \
           and (entry.tag == 'Path' and entry.get('type') == 'directory'):
            # check for any extra entries when prune='true' attribute is set
            try:
                entries = ['/'.join([entry.get('name'), ent]) \
                           for ent in os.listdir(entry.get('name'))]
                ex_ents = [e for e in entries if e not in modlist]
                if ex_ents:
                    pruneTrue = False
                    self.logger.info("POSIX: Directory %s contains "
                                     "extra entries:" % entry.get('name'))
                    self.logger.info(ex_ents)
                    nqtext = entry.get('qtext', '') + '\n'
                    nqtext += "Directory %s contains extra entries: " % \
                              entry.get('name')
                    nqtext += ":".join(ex_ents)
                    entry.set('qtest', nqtext)
                    [entry.append(XML.Element('Prune', path=x)) \
                     for x in ex_ents]
            except OSError:
                ex_ents = []
                pruneTrue = True

        if not pTrue:
            if owner != str(normUid(entry)):
                entry.set('current_owner', owner)
                self.logger.debug("%s %s ownership wrong" % \
                                  (entry.tag, entry.get('name')))
                nqtext = entry.get('qtext', '') + '\n'
                nqtext += "%s owner wrong. is %s should be %s" % \
                          (entry.get('name'), owner, entry.get('owner'))
                entry.set('qtext', nqtext)
            if group != str(normGid(entry)):
                entry.set('current_group', group)
                self.logger.debug("%s %s group wrong" % \
                                  (entry.tag, entry.get('name')))
                nqtext = entry.get('qtext', '') + '\n'
                nqtext += "%s group is %s should be %s" % \
                          (entry.get('name'), group, entry.get('group'))
                entry.set('qtext', nqtext)
            if perms != entry.get('perms'):
                entry.set('current_perms', perms)
                self.logger.debug(
                    "%s %s permissions are %s should be %s" %
                    (entry.tag, entry.get('name'), perms, entry.get('perms')))
                nqtext = entry.get('qtext', '') + '\n'
                nqtext += "%s %s perms are %s should be %s" % \
                          (entry.tag,
                           entry.get('name'),
                           perms,
                           entry.get('perms'))
                entry.set('qtext', nqtext)
            if mtime != entry.get('mtime', '-1'):
                entry.set('current_mtime', mtime)
                self.logger.debug("%s %s mtime is %s should be %s" \
                                  % (entry.tag, entry.get('name'), mtime,
                                     entry.get('mtime')))
                nqtext = entry.get('qtext', '') + '\n'
                nqtext += "%s mtime is %s should be %s" % \
                          (entry.get('name'), mtime, entry.get('mtime'))
                entry.set('qtext', nqtext)
            if entry.get('type') != 'file':
                nnqtext = entry.get('qtext')
                nnqtext += '\nInstall %s %s: (y/N) ' % (entry.get('type'),
                                                        entry.get('name'))
                entry.set('qtext', nnqtext)
        return pTrue and pruneTrue