It depends, are you running the same script as mine from https://gehrcke.de/2013/08/freenas-simple-auto-shutdown-revisited/
If you are, it has two things that need to be changed to work on SCALE one is to convert the Ping function from BSD to Linux, mainly the ‘-o’ It does not exist in Linux Ping. The second is the shutdown command itself. Scale uses ‘mdclt’
There are other possible issues so see notes at the bottom.
Below is my modified working script less IP addresses and log address:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#THIS SCRIPT HAS BEEN MODIFIED TO WORK WITH TRUENAS SCALE ATLEAST TNS 25.X.X
#
# Copyright (C) 2013 Jan-Philip Gehrcke, http://gehrcke.de
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
import os
import sys
import time
import logging
from subprocess import Popen, PIPE
from logging.handlers import RotatingFileHandler
# List of hosts to check for being reachable.
HOSTS_TO_CHECK = [
# "Add your IP and remove the #",
# "Add your IP and remove the #",
# "Add your IP and remove the #",
# "Add your IP and remove the #",
]
# Path to the log file of this script.
logfile_path = "Add you path for the log file"
# Condition for shutdown is that all test hosts have constantly not been
# reachable within a couple of minutes. `REQUIRED_OFFLINE_SECONDS`
REQUIRED_OFFLINE_SECONDS = 300
# `POLLING_INTERVAL_SECONDS` specifies how frequently the list of hosts should
# be checked during the time interval specified above.
POLLING_INTERVAL_SECONDS = 30
# Assert that the required offline time is greater than double the polling interval.
assert REQUIRED_OFFLINE_SECONDS > 2 * POLLING_INTERVAL_SECONDS
def main():
exit_if_any_host_up()
log.info("No host is reachable. Poll again, every %s s.", POLLING_INTERVAL_SECONDS)
deadline = time.time() + REQUIRED_OFFLINE_SECONDS
deadline_str = time.strftime("%H:%M:%S", time.localtime(deadline))
while time.time() < deadline:
log.info('Invoke shutdown if no host comes up until %s.', deadline_str)
time.sleep(POLLING_INTERVAL_SECONDS)
exit_if_any_host_up()
# Modified shutdown command
log.info("'midclt call system.shutdown now' returncode: %s" % run_subprocess(['/usr/bin/midclt', 'call', 'system.shutdown', 'now']))
def exit_if_any_host_up():
log.info("Pinging hosts, exit program if one is up.")
for host in HOSTS_TO_CHECK:
if host_responding(host):
log.info("Exit program.")
sys.exit(0)
def host_responding(host):
log.info("Pinging host '%s'...", host)
rc = run_subprocess(['ping', '-c', '1', '-W', '5', host])
if rc == 0:
log.info("Ping returned with code 0, host is up.")
return True
log.info("Ping returned with code %s, host is down.", rc)
return False
def run_subprocess(cmdlist):
log.debug("Calling Popen(%s).", cmdlist)
try:
sp = Popen(cmdlist, stdout=PIPE, stderr=PIPE)
out, err = sp.communicate()
except OSError as e:
log.error("OSError while executing subprocess. Error message:\n%s" % e)
sys.exit(1)
if out:
log.debug("Subprocess stdout:\n%s", out.decode('utf-8'))
if err:
log.debug("Subprocess stderr:\n%s", err.decode('utf-8'))
return sp.returncode
if __name__ == "__main__":
log = logging.getLogger()
log.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
fh = RotatingFileHandler(logfile_path, mode='a', maxBytes=500*1024, backupCount=30, encoding='utf-8')
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
fh.setFormatter(formatter)
log.addHandler(ch)
log.addHandler(fh)
# Set the file permissions to 770 after the log file is created
os.chmod(logfile_path, 0o770)
main()
The image below is how I have it setup in Cron, not including the time interval in the script:
Notes:
1- I used DuckDuck AI to convert the files. If you haven’t used before I suggest know how your script works, know what’s broken, then get AI to make simple changes at a time giving you the completed script. Then double check what it did. Treat like you are giving a child instructions, It will get it wrong but simple single changes gets you there quicker.
2- If you are using a script such as *.sh then look at my other post to issues I had with Cron and *.sh scripts: https://forums.truenas.com/t/cron-says-success-but-script-does-not-run/56109
3- Back when I tested Scale 22 I had another shutdown script that worked. Far more complex but with more checks and balances such as checking if a Scrub was in progress. Unfortunately It would not work when I actually migrated over to Scale 25 so I abandoned it for the above. easier to fix simple rather than complex, and this one is all I need anyway.
If your script is different and you do manage to get it two work post it for others as options are good.