ElectricEel-24.10.1 Issue

I will look at this Saturday morning (my time).

That (!) is how you run your cron jobs? Why did no one tell me that you can concatenate your commands like that?

I have been using a command like this …

bash ../mnt/SnappitySnap/TrueNASInfo/Scripts/TrueNASInfo.sh
1 Like

Its now working as a cronjob using root

@joeschmuck as said before i was facing same strange issue, running those emails commands as single snipplet,… But in my case the job was killed by system and no email were send. Generating a log file to catch all the error the system was complain about input expected/received, generating a loop of the content of html if It was more than 4kb… no difference between html and plain text.
This happen nor calling internal api and an external service (i use webhook.site).
But the same commands in the full beta script works flawlessy (i removed some parts from the script for not perform continusly smart test)… So my only assumption Is this html_data=$(echo $test_data | jq -Rs .) Is doing something bad/wrong/unexpected out of my knowledge.
Do you need other test/ecc?

You can use midclt call to generate an authentication token (has a TTL) that you then use for REST authentication. There’s no need to use an API key for this. C.f. middleware/src/freenas/etc/find_alias_for_smtplib.py at 811e416a3736579f6d1bab5fc17cfd6b8a8e7cef · truenas/middleware · GitHub

1 Like

Okay, but isn’t the REST API being deprecated in 25.04?

Or is REST authentication something else?

Deprecated doesn’t mean removed.

While true, it seems ill-advised to build on something we know is being removed eventually.
Unless it’s meant as a stopgap while you work on offering a better solution? hint hint, nudge nudge

I was just responding with information and a few pointers to existing (or legacy) scripts. Generally its be better use the python API client for interactions with the middleware. midclt is provided as part of truenas_api_client BTW.

There’s a pretty large working example of scripts using our client in our tests directory middleware/tests/api2 at master · truenas/middleware · GitHub

1 Like

I found a work around (I hope). I don’t like it but until we can figure out why the mail.send API function breaks when run in a CRON Job, I don’t have any other answers.

Let me update the script a bit and I will post if for testing.

3 Likes

Everyone who wants to give this a test, please provide feedback, good, bad, whatever.

What I implemented:

  1. Test which version of TrueNAS is being used, if less than 24.10.1 then use sendmail. If equal or greater than 24.10.1 then force to use curl.
  2. If we are using curl we must check for a user name and password to be entered manually into the script, lines 10 and 11. I personally am fine using actual user name and password however I am also fine using an API key, but I can work that in on the next version, I just want to get this one out the door. When I request a -dump from people, I never attach the original script as it may have personal data in it.
  3. If we are using curl then do we have attachments? If Yes, run normally. If No, create an empty attachment file, push Chart.html file into it, and send the email.

I have been completely unable in sending the html data I wanted in the body of the email using the mail.send API, unless I am running from a CLI/SSH. CRON Jobs will not play nicely.

Cheers!
multi_report_v3.1_2025_01_03_BETA.txt (517.8 KB)

5 Likes

I put a username and password into the top of the script and ran from cron

No useful email here. Script seems to run from cron and I did get an email telling me that the script had run and that it was using curl instead - but no script All is good type email

Running from SSH No email received

Currently running TrueNAS SCALE: ElectricEel-24.10.1

Ran test script from cli as sudo and also via cron. Got the email both times with attached Chart.html.

Noticed that both threw out this:

DANGER Will Robinson, you have 11 drives and 7 authorized days to test.
ROBOT will make adjustments, please wait.... Geez that was fast!
Maybe you need more drives, Waka Waka! ROBOT made a funny.
Recalculation Complete, the new value is 2 drive(s) spread across the 7 authorized days.
 
 
    Drive(s) previously scheduled to be tested: sdd, sdc, sde, sda, sdf, nvme1, nvme3, sdb
    Drive(s) scheduled for testing today: nvme2 nvme0
 
    Drive(s): "nvme0 nvme2" were removed from the Short testing list
    It/They are already scheduled for the Long test, no need to duplicate efforts

So if the first run was supposed to have updated something, it didn’t appear to as the second run reported exactly the same.

Honestly, send email directly with python is so easy… maybe change approach?
Work without issue via CRON too… Downside, everyone must provide a valid smtp account.
This basic code, should work in multi report too (variable should be the same, in case i will check, attachment must for sure be managed)

python3 <<EOF
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders

# SMTP Configuration: maybe standard for everyone or move it on the conf file?
smtp_server = '***' 
smtp_port = 587 
smtp_user = '***'
smtp_password = '***'

subject = '$subject'
to_address = '$Email'
html_file = '$html_file'
attachment_files = ['$html_file', '$html_file']

with open(html_file, 'r') as f:
    html_content = f.read()

msg = MIMEMultipart()
msg['From'] = smtp_user
msg['To'] = to_address
msg['Subject'] = subject
msg.attach(MIMEText(html_content, 'html'))

for attachment_file in attachment_files:
    try:
        with open(attachment_file, 'rb') as f:
            part = MIMEBase('application', 'octet-stream')
            part.set_payload(f.read())
            encoders.encode_base64(part) 
            part.add_header('Content-Disposition', f'attachment; filename="{attachment_file.split("/")[-1]}"')
            msg.attach(part)
    except Exception as e:
        print(f'KO {attachment_file}: {e}')

try:
    server = smtplib.SMTP(smtp_server, smtp_port)
    server.starttls()  
    server.login(smtp_user, smtp_password)  
    server.sendmail(smtp_user, to_address, msg.as_string())  
    server.quit() 
    print('OK')
except Exception as e:
    print(f'KO: {e}')
EOF
1 Like

Being logged in the gui via truenas_admin and running it with sudo from shell, it works.

Running as a cronjob as truenas_admin, it sends half a report (Spinning Rust section is missing), and a second email with an error message:

Maybe the cronjob has to run as root or I have to check which rights the truenas_admin has.

I actually like the attachment thing, it prevents me from immediately scrolling through it.

It is really nice to something that works, especially knowing how difficult it was to get this far, that as been a lot of jumping through hoops while balancing a tray of drinks just to get an email send!

I was looking for some simple python send email code like this … so … thank you. I will try and turn this into a python function / minijob that we can run from a bash script … similar to the disklist.py code that I was using on Core.

Something like this …

#!/bin/bash

# Call Python script with arguments
python3 SendEmail.py arg1 arg2 arg3

Hum, mine is actually working fine, tomorrow I may say something different. Yours looks like a permissions issue. Try running it as root, see if that works. My CRON is root.

@oxyde @ruffhi
I like the Python thing. Not sure if you can utilize API data for the smtp server and authentication. Running it from the script “should” be easy, I run the Spencer python script already, if you have Spencer installed.

Bed time.

Playing around with some python code tonight … while Australia are being bundled out in their first innings against India :frowning:

So far, I have code that can take this …

python3 SendEmail.py smtpinfo.txt emailinfo.txt

The arguments are a smtp file and an email file.

Assumed format for smtp file is …
server, port, user, password

Assumed format for email file is …
To: abc@gmail.com
From: xyz@gmail.com
Subject: “hello world”
htmlfile: filename.html

My python file grabs the two parameters, extracts the smtp info from the smtp file and the email info from the email file and prints this …

smtp server  : server
smtp port    : 587
smtp user    : user
smtp password: password
to       : abc@gmail.com
from     : xyz@gmail.com
subject   : hello world
htmlfile : filename.html

Absolutely no email sending as yet :smiley: but now that I have the parameters, it should be an easy extension.

I am expecting the user to set up the smtp file and the Scale script to construct the email and html files … then call the email python script.

1 Like

@joeschmuck
similar approach of @ruffhi , i create a python script that you can invoke from the MR easily:

#new_sendemail_V1

import smtplib, json, argparse
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders

def read_smtp_config():
   file_path = "smtp.conf"
   try:
       with open(file_path, 'r') as file:
           config = json.load(file)
           required_keys = ["smtp_server", "smtp_port", "smtp_user", "smtp_password"]
           for key in required_keys:
               if key not in config:
                   raise ValueError(f"Required '{key}' not set")
           return config
   except FileNotFoundError:
       raise FileNotFoundError(f"File not found")
   except json.JSONDecodeError:
       raise ValueError(f"File not in json")
   
def send_email(subject, to_address, mail_body_html, attachment_files=None):
   smtp_config = read_smtp_config()

   smtp_server = smtp_config["smtp_server"]
   smtp_port = smtp_config["smtp_port"]
   smtp_user = smtp_config["smtp_user"]
   smtp_password = smtp_config["smtp_password"]

   try:
       with open(mail_body_html, 'r') as f:
           html_content = f.read()

       msg = MIMEMultipart()
       msg['From'] = smtp_user
       msg['To'] = to_address
       msg['Subject'] = subject
       msg.attach(MIMEText(html_content, 'html'))

       if attachment_files:
           for attachment_file in attachment_files:
               try:
                   with open(attachment_file, 'rb') as f:
                       part = MIMEBase('application', 'octet-stream')
                       part.set_payload(f.read())
                       encoders.encode_base64(part)
                       part.add_header('Content-Disposition', f'attachment; filename="{attachment_file.split("/")[-1]}"')
                       msg.attach(part)
               except Exception as e:
                   print(f"KO {attachment_file}: {e}")

       server = smtplib.SMTP(smtp_server, smtp_port)
       server.starttls()
       server.login(smtp_user, smtp_password)
       server.sendmail(smtp_user, to_address, msg.as_string())
       server.quit()
       print("OK")
   except Exception as e:
       print(f"KO: {e}")    

if __name__ == "__main__":
   parser = argparse.ArgumentParser(description="Workaround to send email easily in Multi Report")
   parser.add_argument("--subject", required=True, help="Email subject")
   parser.add_argument("--to_address", required=True, help="Recipient")
   parser.add_argument("--mail_body_html", required=True, help="File path for the email body")
   parser.add_argument("--attachment_files", nargs='*', help="OPTIONAL attachments as json file path array")

   args = parser.parse_args()

   try:
       send_email(
           subject=args.subject,
           to_address=args.to_address,
           mail_body_html=args.mail_body_html,
           attachment_files=args.attachment_files
       )
   except Exception as e:
       print("Error:", e)        

actually im reading the smtp data from a smtp.conf file, json formatted

{
    "smtp_server":""
    ,"smtp_port":""
    ,"smtp_user":""
    ,"smtp_password":""
}

example of usage:

#!/bin/bash
subject=''
Email=''
html_file="" 
attachment=("" "") #as many file you need, closed by " and separeted by space

python3 new_sendemail_V1.py \
    --subject "$subject" \
    --to_address "$Email" \
    --mail_body_html "$html_file" \
    --attachment_files "${attachment[@]}"

Actually is pretty simple, all working expecting to be on the same folder.
Just do some test if you like this solution, because in case i will work on it (there are a lot of things can be made better, like the smtp credential saving)

As expected it was indeed, running the cronjob as root worked!

Multi Report Cron job working fine indeed as Root. Thanks Joeschmuck and all who helped!