Exemple #1
0
    def read(self, lock: bool = False) -> List[_VT]:
        """Parse the file and return the entries"""
        path = self._path

        if lock:
            store.aquire_lock(path)

        entries = []
        try:
            with path.open("rb") as f:
                for entry in f.read().split(b"\0"):
                    if entry:
                        entries.append(
                            self._deserialize(
                                ast.literal_eval(entry.decode("utf-8"))))
        except IOError as e:
            if e.errno == errno.ENOENT:
                pass
            else:
                raise
        except Exception:
            if lock:
                store.release_lock(path)
            raise

        return entries
Exemple #2
0
def rename_host_in_multisite(oldname, newname):
    # State of Multisite ---------------------------------------
    # Favorites of users and maybe other settings. We simply walk through
    # all directories rather then through the user database. That way we
    # are sure that also currently non-existant users are being found and
    # also only users that really have a profile.
    users_changed = 0
    total_changed = 0
    for userid in os.listdir(config.config_dir):
        if userid[0] == '.':
            continue
        if not os.path.isdir(config.config_dir + "/" + userid):
            continue

        favpath = config.config_dir + "/" + userid + "/favorites.mk"
        num_changed = 0
        favorites = store.load_object_from_file(favpath, default=[], lock=True)
        for nr, entry in enumerate(favorites):
            if entry == oldname:
                favorites[nr] = newname
                num_changed += 1
            elif entry.startswith(oldname + ";"):
                favorites[nr] = newname + ";" + entry.split(";")[1]
                num_changed += 1

        if num_changed:
            store.save_object_to_file(favpath, favorites)
            users_changed += 1
            total_changed += num_changed
        store.release_lock(favpath)

    if users_changed:
        return ["favorites"] * total_changed
    return []
Exemple #3
0
def exclusive_lock():
    path = cmk.utils.paths.default_config_dir + "/multisite.mk"
    store.aquire_lock(path)
    try:
        yield
    finally:
        store.release_lock(path)
Exemple #4
0
    def store(
        self,
        *,
        removed: Set[_ValueStoreKey],
        updated: Mapping[_ValueStoreKey, Any],
    ) -> None:
        """Re-load and then store the changes of the item state to disk

        Make sure the object is in sync with the file after writing.
        """
        self._log_debug("Storing item states")
        if not (removed or updated):
            return

        self._path.parent.mkdir(parents=True, exist_ok=True)

        try:
            store.aquire_lock(self._path)
            self._load()

            data = {
                **{k: v
                   for k, v in self._data.items() if k not in removed},
                **updated,
            }
            store.save_object_to_file(self._path, data, pretty=False)
            self._mtime = self._path.stat().st_mtime
            self._data = data
        except Exception:
            raise MKGeneralException(
                f"Cannot write to {self._path}: {traceback.format_exc()}")
        finally:
            store.release_lock(self._path)
Exemple #5
0
 def start(self):
     # type: () -> None
     try:
         store.aquire_lock(self._job_initializiation_lock)
         self._start()
     finally:
         store.release_lock(self._job_initializiation_lock)
Exemple #6
0
    def update_cache(self, cache_id, ipa):
        # type: (IPLookupCacheId, str) -> None
        """Updates the cache with a new / changed entry

        When self.persist_on_update update is disabled, this simply updates the in-memory
        cache without any persistence interaction. Otherwise:

        The cache that was previously loaded into this IPLookupCache with load_persisted()
        might be outdated compared to the current persisted lookup cache. Another process
        might have updated the cache in the meantime.

        The approach here is to load the currently persisted cache with a lock, then update
        the current IPLookupCache with it, add the given new / changed entry and then write
        out the resulting data structure.

        This could really be solved in a better way, but may be sufficient for the moment.

        The cache can only be cleaned up with the "Update DNS cache" option in WATO
        or the "cmk --update-dns-cache" call that both call update_dns_cache().
        """
        if not self.persist_on_update:
            self[cache_id] = ipa
            return

        try:
            self.update(_load_ip_lookup_cache(lock=True))
            self[cache_id] = ipa
            self.save_persisted()
        finally:
            store.release_lock(_cache_path())
Exemple #7
0
    def load(self, lock=False):
        """Parse the site specific changes file

        The file format has been choosen to be able to append changes without
        much cost. This is just a intermmediate format for 1.4.x. In 1.5 we will
        reimplement WATO changes and this very specific file format will vanish."""
        path = self._site_changes_path()

        if lock:
            store.aquire_lock(path)

        changes = []
        try:
            for entry in open(path).read().split("\0"):
                if entry:
                    changes.append(ast.literal_eval(entry))
        except IOError as e:
            if e.errno == errno.ENOENT:
                pass
            else:
                raise
        except:
            if lock:
                store.release_lock(path)
            raise

        return changes
Exemple #8
0
    def acquire(n):
        assert not store.have_lock(path)
        if debug:
            print(f"{n}: Trying lock\n")
        store.aquire_lock(path, blocking=True)
        assert store.have_lock(path)

        # We check to see if the other threads are actually waiting.
        if not saw_someone_wait:
            _wait_for_waiting_lock()
            saw_someone_wait.append(1)

        if debug:
            print(f"{n}: Got lock\n")

        acquired.append(1)
        # This part is guarded by the lock, so we should never have more than one entry in here,
        # even if multiple threads try to append at the same time
        assert len(acquired) == 1

        acquired.pop()
        store.release_lock(path)
        assert not store.have_lock(path)
        if debug:
            print(f"{n}: Released lock\n")
Exemple #9
0
    def load(self) -> None:
        self._log_debug("Loading item states")

        try:
            store.aquire_lock(self._path)
            self._load()
        finally:
            store.release_lock(self._path)
Exemple #10
0
def test_release_lock(locked_file, path_type):
    path = path_type(locked_file)

    assert store.have_lock(path) is False
    store.aquire_lock(path)
    assert store.have_lock(path) is True
    store.release_lock(path)
    assert store.have_lock(path) is False
Exemple #11
0
def test_non_blocking_locking_without_previous_lock(locked_file, path_type, t1):
    assert t1.store != store
    path = path_type(locked_file)

    # Try to lock first
    assert store.try_aquire_lock(path) is True
    assert store.have_lock(path) is True
    store.release_lock(path)
    assert store.have_lock(path) is False
Exemple #12
0
 def acquire(_):
     try:
         store.aquire_lock(path, blocking=False)
         acquired.append(1)
         assert store.have_lock(path)
         store.release_lock(path)
         assert not store.have_lock(path)
     except IOError:
         assert not store.have_lock(path)
Exemple #13
0
def test_release_lock(tmp_path):
    locked_file = tmp_path / "locked_file"
    locked_file.write_text(u"", encoding="utf-8")

    path = str(locked_file)

    assert store.have_lock(path) is False
    store.aquire_lock(path)
    assert store.have_lock(path) is True
    store.release_lock(path)
    assert store.have_lock(path) is False
Exemple #14
0
    def update_status(self, params):
        if not self._jobstatus_path.parent.exists():  # pylint: disable=no-member
            return

        if params:
            try:
                status = store.load_data_from_file(str(self._jobstatus_path), {}, lock=True)
                status.update(params)
                store.save_mk_file(str(self._jobstatus_path), self._format_value(status))
            finally:
                store.release_lock(str(self._jobstatus_path))
Exemple #15
0
def test_release_lock_already_closed(locked_file, path_type):
    path = path_type(locked_file)

    assert store.have_lock(path) is False
    store.aquire_lock(path)
    assert store.have_lock(path) is True

    os.close(store._locks._get_lock(str(path)))  # pylint:disable=no-value-for-parameter

    store.release_lock(path)
    assert store.have_lock(path) is False
Exemple #16
0
    def update_status(self, params: JobStatusSpec) -> None:
        if not self._jobstatus_path.parent.exists():
            return

        if params:
            try:
                status = store.load_object_from_file(str(self._jobstatus_path), {}, lock=True)
                status.update(params)
                store.save_mk_file(str(self._jobstatus_path), self._format_value(status))
            finally:
                store.release_lock(str(self._jobstatus_path))
Exemple #17
0
def test_release_lock(tmpdir):
    locked_file = tmpdir.join("locked_file")
    locked_file.write("")

    path = "%s" % locked_file

    assert store.have_lock(path) is False
    store.aquire_lock(path)
    assert store.have_lock(path) is True
    store.release_lock(path)
    assert store.have_lock(path) is False
Exemple #18
0
def test_release_lock_already_closed(locked_file, path_type):
    path = path_type(locked_file)

    assert store.have_lock(path) is False
    store.aquire_lock(path)
    assert store.have_lock(path) is True

    os.close(store._locks._get_lock(str(path)))

    store.release_lock(path)
    assert store.have_lock(path) is False
Exemple #19
0
def _cleanup_locked_pid_file(path: Path) -> None:
    """Cleanup the lock + file acquired by the function above"""
    if not store.have_lock(str(path)):
        return

    store.release_lock(str(path))

    try:
        path.unlink()
    except OSError:
        pass
Exemple #20
0
 def load(self, hostname: HostName) -> None:
     filename = cmk.utils.paths.counters_dir + "/" + hostname
     try:
         # TODO: refactoring. put these two values into a named tuple
         self._item_states = store.load_object_from_file(
             filename,
             default={},
             lock=True,
         )
         self._last_mtime = os.stat(filename).st_mtime
     finally:
         store.release_lock(filename)
Exemple #21
0
def test_release_lock_already_closed(locked_file, path_type):
    path = path_type(locked_file)

    assert store.have_lock(path) is False
    store.aquire_lock(path)
    assert store.have_lock(path) is True

    fd = store._locks._get_lock(str(path))
    assert isinstance(fd, int)
    os.close(fd)

    store.release_lock(path)
    assert store.have_lock(path) is False
Exemple #22
0
def test_release_lock_already_closed(tmp_path):
    locked_file = tmp_path / "locked_file"
    locked_file.write_text(u"", encoding="utf-8")

    path = str(locked_file)

    assert store.have_lock(path) is False
    store.aquire_lock(path)
    assert store.have_lock(path) is True

    os.close(store._acquired_locks[path])

    store.release_lock(path)
    assert store.have_lock(path) is False
Exemple #23
0
def test_release_lock_already_closed(tmpdir):
    locked_file = tmpdir.join("locked_file")
    locked_file.write("")

    path = "%s" % locked_file

    assert store.have_lock(path) is False
    store.aquire_lock(path)
    assert store.have_lock(path) is True

    os.close(store._acquired_locks[path])

    store.release_lock(path)
    assert store.have_lock(path) is False
Exemple #24
0
    def save_change(self, change_spec):
        path = self._site_changes_path()
        try:
            store.aquire_lock(path)

            with open(path, "a+") as f:
                f.write(repr(change_spec) + "\0")
                f.flush()
                os.fsync(f.fileno())

            os.chmod(path, 0660)

        except Exception as e:
            raise MKGeneralException(_("Cannot write file \"%s\": %s") % (path, e))

        finally:
            store.release_lock(path)
Exemple #25
0
    def save_change(self, change_spec):
        path = self._site_changes_path()
        try:
            store.aquire_lock(path)

            with path.open("ab+") as f:
                f.write(six.ensure_binary(repr(change_spec)) + b"\0")
                f.flush()
                os.fsync(f.fileno())

            path.chmod(0o660)

        except Exception as e:
            raise MKGeneralException(_("Cannot write file \"%s\": %s") % (path, e))

        finally:
            store.release_lock(path)
Exemple #26
0
    def append(self, entry: _VT) -> None:
        path = self._path
        try:
            store.aquire_lock(path)

            with path.open("ab+") as f:
                f.write(repr(entry).encode("utf-8") + b"\0")
                f.flush()
                os.fsync(f.fileno())

            path.chmod(0o660)

        except Exception as e:
            raise MKGeneralException(_("Cannot write file \"%s\": %s") % (path, e))

        finally:
            store.release_lock(path)
Exemple #27
0
    def disksync(
            self,
            *,
            removed: Container[_TKey] = (),
            updated: Iterable[Tuple[_TKey, _TValue]] = (),
    ) -> None:
        """Re-load and write the changes of the stored values

        This method will reload the values from disk, apply the changes (remove keys
        and update values) as specified by the arguments, and then write the result to disk.

        When this method returns, the data provided via the Mapping-interface and
        the data stored on disk must be in sync.
        """
        self._log_debug("synchronizing")

        self._path.parent.mkdir(parents=True, exist_ok=True)

        try:
            store.aquire_lock(self._path)

            if self._path.stat().st_mtime == self._last_sync:
                self._log_debug("already loaded")
            else:
                self._log_debug("loading from disk")
                self._data = self._deserializer(
                    store.load_text_from_file(self._path,
                                              default="{}",
                                              lock=False))

            if removed or updated:
                data = {
                    k: v
                    for k, v in self._data.items() if k not in removed
                }
                data.update(updated)
                self._log_debug("writing to disk")
                store.save_text_to_file(self._path, self._serializer(data))
                self._data = data

            self._last_sync = self._path.stat().st_mtime
        except Exception as exc:
            raise MKGeneralException from exc
        finally:
            store.release_lock(self._path)
Exemple #28
0
    def get_status_from_file(self) -> JobStatusSpec:
        if not self._jobstatus_path.exists():
            data: JobStatusSpec = {}
            data["state"] = JobStatusStates.INITIALIZED
            data["started"] = time.time()
        else:
            try:
                # Read this data with an explicit lock
                # This prevents a race condition where an empty jobstatus.mk file is read
                data = store.load_object_from_file(str(self._jobstatus_path),
                                                   default={},
                                                   lock=True)

                # Repair broken/invalid files
                if "state" not in data:
                    data["state"] = JobStatusStates.INITIALIZED
                    data["started"] = os.path.getctime(
                        str(self._jobstatus_path))
            finally:
                store.release_lock(str(self._jobstatus_path))

        data.setdefault("pid", None)
        data["loginfo"] = {}
        for field_id, field_path in [
            ("JobProgressUpdate", self._progress_update_path),
            ("JobResult", self._result_message_path),
            ("JobException", self._exceptions_path),
        ]:
            if field_path.exists():
                with field_path.open(encoding="utf-8") as f:
                    data["loginfo"][field_id] = f.read().splitlines()
            else:
                data["loginfo"][field_id] = []

        map_substate_to_active = {
            JobStatusStates.INITIALIZED: True,
            JobStatusStates.RUNNING: True,
            JobStatusStates.FINISHED: False,
            JobStatusStates.STOPPED: False,
            JobStatusStates.EXCEPTION: False,
        }
        data["is_active"] = map_substate_to_active[data["state"]]
        return data
Exemple #29
0
    def disksync(
            self,
            *,
            removed: Container[_ValueStoreKey] = (),
            updated: Iterable[Tuple[_ValueStoreKey, Any]] = (),
    ) -> None:
        """Re-load and then store the changes of the item state to disk

        Make sure the object is in sync with the file after writing.
        """
        self._log_debug("value store: synchronizing")

        self._path.parent.mkdir(parents=True, exist_ok=True)

        try:
            store.aquire_lock(self._path)

            if self._path.stat().st_mtime == self._last_sync:
                self._log_debug("value store: already loaded")
            else:
                self._log_debug("value store: loading from disk")
                self._data = store.load_object_from_file(self._path,
                                                         default={},
                                                         lock=False)

            if removed or updated:
                data = {
                    k: v
                    for k, v in self._data.items() if k not in removed
                }
                data.update(updated)
                self._log_debug("value store: writing to disk")
                store.save_object_to_file(self._path, data, pretty=False)
                self._data = data

            self._last_sync = self._path.stat().st_mtime
        except Exception as exc:
            raise MKGeneralException from exc
        finally:
            store.release_lock(self._path)
Exemple #30
0
    def save(self, hostname):
        # type: (HostName) -> None
        """ The job of the save function is to update the item state on disk.
        It simply returns, if it detects that the data wasn't changed at all since the last loading
        If the data on disk has been changed in the meantime, the cached data is updated from disk.
        Afterwards only the actual modifications (update/remove) are applied to the updated cached
        data before it is written back to disk.
        """
        filename = cmk.utils.paths.counters_dir + "/" + hostname
        if not self._removed_item_state_keys and not self._updated_item_states:
            return

        try:
            if not os.path.exists(cmk.utils.paths.counters_dir):
                os.makedirs(cmk.utils.paths.counters_dir)

            store.aquire_lock(filename)
            last_mtime = os.stat(filename).st_mtime
            if last_mtime != self._last_mtime:
                self._item_states = store.load_object_from_file(filename,
                                                                default={})

                # Remove obsolete keys
                for key in self._removed_item_state_keys:
                    try:
                        del self._item_states[key]
                    except KeyError:
                        pass

                # Add updated keys
                self._item_states.update(self._updated_item_states)

            store.save_object_to_file(filename,
                                      self._item_states,
                                      pretty=False)
        except Exception:
            raise MKGeneralException("Cannot write to %s: %s" %
                                     (filename, traceback.format_exc()))
        finally:
            store.release_lock(filename)