diff options
| author | jwansek <eddie.atten.ea29@gmail.com> | 2025-07-27 00:12:22 +0100 | 
|---|---|---|
| committer | jwansek <eddie.atten.ea29@gmail.com> | 2025-07-27 00:12:22 +0100 | 
| commit | 9035a17e5126d7bcc54bf42a65ac291573078d8e (patch) | |
| tree | 69b8a9337c7fe45de26382dfc2116ae4f78d37a4 /autoBackup | |
| parent | e236023989ecf9caf4b27d7f6686f67724ac75d0 (diff) | |
| download | BetterZFSReplication-9035a17e5126d7bcc54bf42a65ac291573078d8e.tar.gz BetterZFSReplication-9035a17e5126d7bcc54bf42a65ac291573078d8e.zip | |
Added autoScrub script to run monthly
Diffstat (limited to 'autoBackup')
| -rw-r--r-- | autoBackup/.env.example | 1 | ||||
| -rw-r--r-- | autoBackup/Dockerfile | 1 | ||||
| -rw-r--r-- | autoBackup/autoBackup.py | 63 | ||||
| -rw-r--r-- | autoBackup/autoScrub.py | 71 | 
4 files changed, 113 insertions, 23 deletions
| diff --git a/autoBackup/.env.example b/autoBackup/.env.example index 0dea6fc..af58add 100644 --- a/autoBackup/.env.example +++ b/autoBackup/.env.example @@ -7,6 +7,7 @@ SLAVE_KEY==*****************************************************************  SLAVE_USERNAME=************  SLAVE_PASSWORD=***************  SLAVE_REPLICATION_TASKS=localVMs/localVMs - fivehundred/localVMs,ReplicateDatabaseBackups +SLAVE_SCRUB_POOLS=fivehundred,localVMs,chud  POLLING_RATE=300 diff --git a/autoBackup/Dockerfile b/autoBackup/Dockerfile index 1ae96da..edcaf84 100644 --- a/autoBackup/Dockerfile +++ b/autoBackup/Dockerfile @@ -9,5 +9,6 @@ RUN pip3 install -r requirements.txt  RUN pip3 install -r TasmotaCLI/requirements.txt  RUN echo "0 21 * * sat,wed root python3 /app/autoBackup.py > /proc/1/fd/1 2>/proc/1/fd/2" > /etc/crontab +RUN echo "@monthly root python3 /app/autoScrub.py > /proc/1/fd/1 2>/proc/1/fd/2" >> /etc/crontab  ENTRYPOINT ["bash"]  CMD ["entrypoint.sh"]
\ No newline at end of file diff --git a/autoBackup/autoBackup.py b/autoBackup/autoBackup.py index b005492..559e49c 100644 --- a/autoBackup/autoBackup.py +++ b/autoBackup/autoBackup.py @@ -1,5 +1,6 @@  import truenas_api_client -import dataclasses +import subprocess +import datetime  import requests  import logging  import pickle @@ -63,19 +64,19 @@ class TrueNASWebsocketsClient(truenas_api_client.JSONRPCClient):          super().__exit__(*args, **kwargs)          logging.info("%s Websocket disconnected" % self.host) -    def __get_ser_name(self): -        return ".%s_replication_jobs.pickle" % self.host +    def _get_job_serialized_name(self, job_type): +        return os.path.join(os.path.dirname(__file__), ".%s_%s_jobs.pickle" % (self.host, job_type)) -    def __get_running_replication_jobs_ser(self): -        if os.path.exists(self.__get_ser_name()): -            with open(self.__get_ser_name(), "rb") as f: +    def _get_serialized_jobs(self, job_type): +        if os.path.exists(self._get_job_serialized_name(job_type)): +            with open(self._get_job_serialized_name(job_type), "rb") as f:                  return pickle.load(f)          else:              return {} -    def __set_running_replication_jobs_ser(self, running_replication_jobs): -        with open(self.__get_ser_name(), "wb") as f: -            pickle.dump(running_replication_jobs, f) +    def _set_serialized_jobs(self, jobs, job_name): +        with open(self._get_job_serialized_name(job_name), "wb") as f: +            pickle.dump(jobs, f)      def get_replication_tasks(self):          return list(filter(lambda a: a["name"] in self.replication_task_names, self.call("replication.query"))) @@ -96,31 +97,44 @@ class TrueNASWebsocketsClient(truenas_api_client.JSONRPCClient):          return self.call("system.shutdown", "Automatic autoBackup shutdown")      def run_all_replication_tasks(self): -        running_replication_jobs = self.__get_running_replication_jobs_ser() +        running_replication_jobs = self._get_serialized_jobs("replication")          for task in self.get_replication_tasks():              job_id = self.run_replication_task(task["id"])              running_replication_jobs[job_id] = task["name"]              logging.info("Started replication task '%s' on '%s' with job id %d" % (task["name"], self.host, job_id)) -        self.__set_running_replication_jobs_ser(running_replication_jobs) +        self._set_serialized_jobs(running_replication_jobs, "replication") + +    def scrub_pools(self, pools): +        running_jobs = self._get_serialized_jobs("scrub") + +        for pool_name in pools: +            job_id = self.call("pool.scrub.scrub", pool_name) +            running_jobs[job_id] = pool_name +            logging.info("Started scrub job on pool '%s' on host '%s' with job id %d" % (pool_name, self.host, job_id)) + +        self._set_serialized_jobs(running_jobs, "scrub")      def get_jobs(self):          return self.call("core.get_jobs") - +          def get_state_of_replication_jobs(self): -        running_replication_jobs = self.__get_running_replication_jobs_ser() +        return self.get_state_of_jobs("replication") + +    def get_state_of_jobs(self, job_type): +        running_jobs = self._get_serialized_jobs(job_type)          all_complete = True          for job in self.get_jobs(): -            if job["id"] in running_replication_jobs.keys(): +            if job["id"] in running_jobs.keys():                  if job["state"] == "RUNNING":                      all_complete = False -                logging.info("Replication job '%s' on '%s' is currently '%s' (%d%%)" % ( -                    running_replication_jobs[job["id"]], self.host, job["state"], job["progress"]["percent"] +                logging.info("%s job '%s' on '%s' is currently '%s' (%d%%)" % ( +                    job_type, running_jobs[job["id"]], self.host, job["state"], job["progress"]["percent"]                  ))          if all_complete: -            os.remove(self.__get_ser_name()) +            os.remove(self._get_job_serialized_name(job_type))              logging.info("No more running replication jobs on '%s'" % self.host)          return all_complete @@ -267,6 +281,9 @@ def wait_till_idle_power():              break  def main(): +    start_time = datetime.datetime.now() +    subprocess.run(["rm", "-f", os.path.join(os.path.dirname(__file__), "*_replication_jobs.pickle")]) +      if os.environ["MASTER_REPLICATION_TASKS"] != "":          tasks = os.environ["MASTER_REPLICATION_TASKS"].split(",")      else: @@ -329,13 +346,13 @@ def main():          ) as slave:              logging.info(json.dumps(slave.shutdown(), indent = 4)) -    # wait until the slave TrueNAS is using 0w of power, which implies it has finished shutting down, -    # then turn off the power to it -    wait_till_idle_power() -    get_mqtt("OFF") -    logging.info("Turned off the slave's plug") +        # wait until the slave TrueNAS is using 0w of power, which implies it has finished shutting down, +        # then turn off the power to it +        wait_till_idle_power() +        get_mqtt("OFF") +        logging.info("Turned off the slave's plug") -    logging.info("autoBackup procedure completed\n\n") +    logging.info("autoBackup backup procedure completed. Took %s\n\n" % str(datetime.datetime.now() - start_time))  if __name__ == "__main__":      main() diff --git a/autoBackup/autoScrub.py b/autoBackup/autoScrub.py new file mode 100644 index 0000000..c34f5aa --- /dev/null +++ b/autoBackup/autoScrub.py @@ -0,0 +1,71 @@ +import subprocess +from autoBackup import TrueNASWebsocketsClient, get_mqtt, wait_for_sockets_slave, wait_till_idle_power +import datetime +import logging +import time +import json +import os + +def main(): +    start_time = datetime.datetime.now() +    subprocess.run(["rm", "-f", os.path.join(os.path.dirname(__file__), "*_scrub_jobs.pickle")]) + +    if os.environ["SLAVE_SCRUB_POOLS"] != "": +        scrub_pools = os.environ["SLAVE_SCRUB_POOLS"].split(",") +    else: +        scrub_pools = [] + +    logging.info("Began autoScrub scrub procedure") +    m = get_mqtt() +    logging.info("Slave plug '%s' is currently %s" % (m.friendlyname, m.switch_power)) +    if m.switch_power == "ON": +        was_already_on = True +    else: +        was_already_on = False +        get_mqtt("ON") +        logging.info("Turned on the slave plug. Now waiting for it to boot") +        # wait_for_slave(slave) +        wait_for_sockets_slave() + +    with TrueNASWebsocketsClient( +        host = os.environ["SLAVE_HOST"],  +        username = os.environ["SLAVE_USERNAME"], +        password = os.environ["SLAVE_PASSWORD"] +    ) as slave: +        slave.scrub_pools(scrub_pools) + +    while True: +        with TrueNASWebsocketsClient( +            host = os.environ["SLAVE_HOST"],  +            username = os.environ["SLAVE_USERNAME"], +            password = os.environ["SLAVE_PASSWORD"] +        ) as slave: +            if slave.get_state_of_jobs("scrub"): +                break + +        logging.info("Slave plug '%s' is using %dw of power" % (os.environ["SLAVE_PLUG_FRIENDLYNAME"], get_mqtt().switch_energy['Power'])) +        time.sleep(int(os.environ["POLLING_RATE"])) + +    logging.info("All scrub jobs on all hosts complete") + +    if was_already_on: +        logging.info("The slave TrueNAS was turned on not by us, so stopping here") +    else: +        logging.info("The slave TrueNAS was turned on by us, so starting the shutdown procedure") +        with TrueNASWebsocketsClient( +            host = os.environ["SLAVE_HOST"],  +            username = os.environ["SLAVE_USERNAME"], +            password = os.environ["SLAVE_PASSWORD"], +        ) as slave: +            logging.info(json.dumps(slave.shutdown(), indent = 4)) + +        # wait until the slave TrueNAS is using 0w of power, which implies it has finished shutting down, +        # then turn off the power to it +        wait_till_idle_power() +        get_mqtt("OFF") +        logging.info("Turned off the slave's plug") + +    logging.info("autoScrub scrub procedure completed. Took %s\n\n" % str(datetime.datetime.now() - start_time)) + +if __name__ == "__main__": +    main()
\ No newline at end of file | 
