I just started using Airflow, can anyone enlighten me how to pass a parameter into PythonOperator like below:
t5_send_notification = PythonOperator(
task_id='t5_send_notification',
provide_context=True,
python_callable=SendEmail,
op_kwargs=None,
#op_kwargs=(key1='value1', key2='value2'),
dag=dag,
)
def SendEmail(**kwargs):
msg = MIMEText("The pipeline for client1 is completed, please check.")
msg['Subject'] = "xxxx"
msg['From'] = "xxxx"
......
s = smtplib.SMTP('localhost')
s.send_message(msg)
s.quit()
I would like to be able to pass some parameters into the t5_send_notification's callable which is SendEmail, ideally I want to attach the full log and/or part of the log (which is essentially from the kwargs) to the email to be sent out, guessing the t5_send_notification is the place to gather those information.
Thank you very much.
Pass a dict object to op_kwargs
Use the keys to access their value from kwargs dict in your python callable
def SendEmail(**kwargs):
print(kwargs['key1'])
print(kwargs['key2'])
msg = MIMEText("The pipeline for client1 is completed, please check.")
msg['Subject'] = "xxxx"
msg['From'] = "xxxx"
......
s = smtplib.SMTP('localhost')
s.send_message(msg)
s.quit()
t5_send_notification = PythonOperator(
task_id='t5_send_notification',
provide_context=True,
python_callable=SendEmail,
op_kwargs={'key1': 'value1', 'key2': 'value2'},
dag=dag,
)
PythonOperator have a named parameter op_kwargs and accepts dict object.
have
t5_send_notification = PythonOperator(
task_id='t5_send_notification',
provide_context=True,
python_callable=SendEmail,
op_kwargs={"my_param":'value1'},
dag=dag,
)
def SendEmail(my_param,**kwargs):
print(my_param) #'value_1'
msg = MIMEText("The pipeline for client1 is completed, please check.")
msg['Subject'] = "xxxx"
msg['From'] = "xxxx"
......
s = smtplib.SMTP('localhost')
s.send_me
Related
I'm using pika to send message to rabbitmq.
I need to send additional properties so I'm using pika.BasicProperties, but when I see this message in Wireshark there are no properties added.
My code:
import pika, uuid
connection = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
channel.queue_declare(queue='test_60', durable=True, arguments={'x-message-ttl' : 21600000})
routingKey = "test_server"
message = '{"test_int":268,"Timestamp":1610022012203}'
correlation_id = str(uuid.uuid4())
reply_to = "test_60"
message_type = "SendRequest"
channel.basic_publish(exchange='',routing_key=routingKey,
body=message,properties=pika.BasicProperties(
headers={"testId": "60"},
delivery_mode=2,
correlation_id = correlation_id,
reply_to = reply_to,
type = message_type))
print('message sent')
print(correlation_id)
In Wireshark this message looks like this, so there are no properties and I have no idea what is wrong with this example.
prop = pika.BasicProperties(
content_type='application/json',
content_encoding='utf-8',
headers={'key': 'value'},
delivery_mode = 1,
)
channel.basic_publish(
exchange='',
routing_key=qname,
properties=prop,
body='{message: hello}'
)
UI:
Wireshark:
I'm trying to test the following function
def send_mail(config, message, raw_object):
smtp_config = config['handlers']['smtp']
session = smtplib.SMTP(smtp_config['host'], smtp_config['port'])
if smtp_config['tls']:
session.starttls()
session.login(smtp_config['from'], smtp_config['password'])
for to in smtp_config['to']:
mail = MIMEMultipart()
mail['From'] = smtp_config['from']
mail['To'] = to
mail['Subject'] = message
body = yaml.safe_dump(raw_object)
mail.attach(MIMEText(body, 'plain'))
try:
session.sendmail(smtp_config['from'], to, mail.as_string())
logging.info(f"Handler:SMTP {to}: {message}")
except smtplib.SMTPException as exc:
logging.error("SMTPException:")
logging.error(exc)
session.quit()
I have the following test
from unittest import TestCase
from unittest.mock import patch
from kubewatcher.handlers import send_mail
class Test(TestCase):
#patch("smtplib.SMTP")
def test_handle__send_mail(self, smtp):
from_ = "from"
password = "password"
host = "host"
port = 587
tls = True
to = ["to"]
config = {
"handlers": {
"smtp": {
"from": from_,
"password": password,
"host": host,
"port": port,
"tls": tls,
"to": to
}
}
}
message = "message"
raw_object = {}
send_mail(config, message, raw_object)
smtp.assert_called_once_with(host, port)
smtp.starttls.assert_called_once()
smtp.login.assert_called_once_with(from_, password)
The first assertion, smtp.assert_called_once_with(host, port), works just fine. But the entire test fails with the following error
...
AssertionError: Expected 'starttls' to have been called once. Called 0 times.
Here's the code you're testing:
session = smtplib.SMTP(smtp_config['host'], smtp_config['port'])
if smtp_config['tls']:
session.starttls()
session.login(smtp_config['from'], smtp_config['password'])
This test is working:
smtp = patch("smtplib.SMTP") # Sorta; this is just shorthand
smtp.assert_called_once_with(host, port)
The problem is here:
smtp.starttls.assert_called_once()
But it's actually correct for it to fail. Your code isn't calling smtplib.SMTP.starttls, but session.starttls, which is the thing that smtplib.SMTP returns.
You can work around that with something like:
from unittest.mock import patch, Mock
mock_session = Mock() # This is the thing we'll be inspecting later
smtp.return_value = mock_session # `smtplib.SMTP` will return this object
send_mail(config, message, raw_object)
smtp.assert_called_once_with(host, port)
mock_session.starttls.assert_called_once()
mock_session.login.assert_called_once_with(from_, password)
Below is my code and I am hoping someone can help me with the cleaning up the code and making it more effiencient. Basically, the code should iterate through all the volumes in my AWS account and then list all untagged volumes and then send out an email. However, it times out when running it as a lambda function in AWS but if i run it locally, it will take over 30 mins to complete (however it does complete). Im sure its iterating through things it doesnt need.
Also if I print the ec2_instances list, I can see duplicate values, so I want to only have unique values so that its not repeating the script for each ec2 instance.
import logging
import boto3
from smtplib import SMTP, SMTPException
from email.mime.text import MIMEText
logger = logging.getLogger()
logger.setLevel(logging.INFO)
session = boto3.Session(profile_name="prod")
client = session.client('ec2')
untagged_volumes = []
detached_volumes = []
ec2_instances = []
response = client.describe_volumes()
for volume in response['Volumes']:
if 'Tags' in str(volume):
continue
else:
if 'available' in str(volume):
detached_volumes.append(volume['VolumeId'])
else:
untagged_volumes.append(volume['VolumeId'])
untagged_volumes.append(volume['Attachments'][0]['InstanceId'])
ec2_instances.append(volume['Attachments'][0]['InstanceId'])
unique_instances = list(set(ec2_instances))
# Create the msg body.
msg_body_list = []
for instance in unique_instances:
desc_instance = client.describe_instances()
# append to the msg_body_list the lines that we would like to show on the email
msg_body_list.append("VolumeID: {}".format(desc_instance['Reservations'][0]['Instances'][0]['BlockDeviceMappings'][0]['Ebs']['VolumeId']))
msg_body_list.append("Attached Instance: {}".format(desc_instance['Reservations'][0]['Instances'][0]['InstanceId']))
# if there are tags, we will append it as singles lines as far we have tags
if 'Tags' in desc_instance['Reservations'][0]['Instances'][0]:
msg_body_list.append("Tags:")
for tag in desc_instance['Reservations'][0]['Instances'][0]['Tags']:
msg_body_list.append(" Key: {} | Value: {}".format(tag['Key'], tag['Value']))
# in case we don't have tags, just append no tags.
else:
msg_body_list.append("Tags: no tags")
msg_body_list.append("--------------------")
# send email
mail_from = "xxx#xxx.com"
mail_to = 'xxx#xxx.com'
msg = MIMEText("\n".join(msg_body_list))
msg["Subject"] = "EBS Tagged Instance Report for"
msg["From"] = mail_from
msg["To"] = mail_to
try:
server = SMTP('xxx.xxx.xxx.xxx', 'xx')
server.sendmail(mail_from, mail_to.split(','), msg.as_string())
server.quit()
print('Email sent')
except SMTPException:
print('ERROR! Unable to send mail')
Lambda functions have a time limit of 15 minutes. That is the reason for the timeout - if you need to run scripts for longer, look up AWS Fargate.
As we get run_id in Airflow, how to get timestamp(ts)?
First:
In your task set provide_context=True
bye_operator = PythonOperator(
task_id='bye_task',
python_callable=print_goodbye,
provide_context=True,
dag=dag
)
Second:
Ensure you are passing the known arguments into your callback function:
def print_goodbye(**kwargs):
ts = kwargs.get('ts', None)
print(ts)
return 'Good bye world!'
I am using SendGrid for Python. I want to CC some people in an email. It seems like they may no longer support CC'ing on emails, though I'm not positive if that's true? But surely there is a work around to it somehow, but I am surprised I can't find much support on this.
Here is my basic code:
sg = sendgrid.SendGridAPIClient(apikey='*****')
from_email = Email(sender_address, sender_name)
to_email = Email(email_address)
subject = subject
content = Content("text/plain", email_message)
mail = Mail(from_email, subject, to_email, content)
response = sg.client.mail.send.post(request_body=mail.get())
How can I modify this so it will CC someone on an email?
Using the SendGrid's Personalization() or Email() class did not work for me. This is how I got it to work:
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail, Cc
# using a list of tuples for emails
# e.g. [('email1#example.com', 'email1#example.com'),('email2#example.com', 'email2#example.com')]
to_emails = []
for r in recipients:
to_emails.append((r, r))
# note the Cc class
cc_emails = []
for c in cc:
cc_emails.append(Cc(c, c))
message = Mail(
from_email=from_email,
to_emails=to_emails,
subject='My Subject',
html_content=f'<div>My HTML Email...</div>'
)
if cc_emails:
message.add_cc(cc_emails)
try:
sg = SendGridAPIClient(os.getenv('SENDGRID_API_KEY'))
sg.send(message)
except Exception as e:
print(f'{e}')
Hopefully this helps someone.
I resolved it. Santiago's answer got me mostly there, but here is what I needed to do:
sg = sendgrid.SendGridAPIClient(apikey='****')
from_email = Email(sender_address, sender_name)
to_email = Email(to_email)
cc_email = Email(cc_email)
p = Personalization()
p.add_to(to_email)
p.add_cc(cc_email)
subject = subject
content = Content("text/plain", email_message)
mail = Mail(from_email, subject, to_email, content)
mail.add_personalization(p)
response = sg.client.mail.send.post(request_body=mail.get())
If you don't include the p.add_to(to_email) it rejects it because there is no "to email" in the personalization object. Also, if you don't include the "to_email" inside the mail object it rejects it because it is looking for that argument, so you have to be a bit redundant and define it twice.
I've been looking at the code: https://github.com/sendgrid/sendgrid-python/blob/master/examples/mail/mail.py
And it looks like you can do that by adding a personalization to the mail, for example:
cc_email = Email(cc_address)
p = Personalization()
p.add_cc(cc_email)
mail.add_personalization(p)
Based on the answers here you can CC to email if you add another email to 'to_email'.
If you want to cc multiple user then in djanogo using sendgrid you need to import the below line
the function that will be used to send the mail
and finally how you ned to send the data paramters to the above function so that it can CC the person
email = send_sandgridmail(sender=sender,receiver=receivers,subject=subject,content=message,reply_to=sender,cc=[admin_mail_account_mail,"rawatanup918#gmail.com"],attachment=None)
i hope this'll help.simplified from #anurag image script
import os
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import To,Mail,ReplyTo,Email,Cc
def send_sandgridmail (sender, receiver, subject, content, reply_to=None, cc=[], attachment=None) :
# content = convert_safe_text(content)
# to email = To(receiver)
message = Mail(
from_email=str(sender),
to_emails=receiver,
subject= str(subject),
html_content = content)
if reply_to:
message.reply_to= ReplyTo(reply_to)
if attachment:
message.add_attachment (attachment)
if len(cc):
cc_mail = []
for cc_person in cc:
cc_mail.append(Cc(cc_person, cc_person))
message.add_cc (cc_mail)
try:
SENDGRID_API_KEY = 'your sendgrid api key'
sg= SendGridAPIClient (SENDGRID_API_KEY)
response= sg.send(message)
print (response.status_code)
# print (response.body)
# print (response.headers)
except Exception as e:
print(e)
return response