def _send( # pylint: disable=arguments-differ self, csv_name, output_filename, now_utc, violation_errors, total_violations, resource_summaries, email_sender, email_recipient, email_description): """Send a summary email of the scan. Args: csv_name (str): The full path of the local csv filename. output_filename (str): The output filename. now_utc (datetime): The UTC datetime right now. violation_errors (iterable): Iterable of violation errors. total_violations (int): The total violations. resource_summaries (dict): Maps resource to violations. {'organization': {'pluralized_resource_type': 'Organizations', 'total': 1, 'violations': OrderedDict([('660570133860', 67)])}, 'project': {'pluralized_resource_type': 'Projects', 'total': 41, 'violations': OrderedDict([('foo1_project', 111), ('foo2_project', 222), ('foo3_project', 333)])}} email_sender (str): The sender of the email. email_recipient (str): The recipient of the email. email_description (str): Brief scan description to include in the subject of the email, e.g. 'Policy Scan'. """ # Render the email template with values. scan_date = now_utc.strftime('%Y %b %d, %H:%M:%S (UTC)') email_content = EmailUtil.render_from_template( 'scanner_summary.jinja', { 'scan_date': scan_date, 'resource_summaries': resource_summaries, 'violation_errors': violation_errors, }) # Create an attachment out of the csv file and base64 encode the # content. attachment = EmailUtil.create_attachment( file_location=csv_name, content_type='text/csv', filename=output_filename, disposition='attachment', content_id='Scanner Violations' ) scanner_subject = '{} Complete - {} violation(s) found'.format( email_description, total_violations) try: self.email_util.send( email_sender=email_sender, email_recipient=email_recipient, email_subject=scanner_subject, email_content=email_content, content_type='text/html', attachment=attachment) except util_errors.EmailSendError: LOGGER.warn('Unable to send Scanner Summary email')
def _send_email(csv_name, now_utc, all_violations, total_resources, violation_errors): """Send a summary email of the scan. Args: csv_name: The full path of the csv. now_utc: The UTC datetime right now. all_violations: The list of violations. total_resources: A dict of the resources and their count. violation_errors: Iterable of violation errors. """ mail_util = EmailUtil(FLAGS.sendgrid_api_key) total_violations, resource_summaries = _build_scan_summary( all_violations, total_resources) # Render the email template with values. scan_date = now_utc.strftime('%Y %b %d, %H:%M:%S (UTC)') email_content = EmailUtil.render_from_template( 'scanner_summary.jinja', { 'scan_date': scan_date, 'resource_summaries': resource_summaries, 'violation_errors': violation_errors, }) # Create an attachment out of the csv file and base64 encode the content. attachment = EmailUtil.create_attachment( file_location=csv_name, content_type='text/csv', filename=_get_output_filename(now_utc), disposition='attachment', content_id='Scanner Violations') scanner_subject = 'Policy Scan Complete - {} violation(s) found'.format( total_violations) mail_util.send(email_sender=FLAGS.email_sender, email_recipient=FLAGS.email_recipient, email_subject=scanner_subject, email_content=email_content, content_type='text/html', attachment=attachment)
class EmailViolationsPipeline(bnp.BaseNotificationPipeline): """Email pipeline to perform notifications""" def __init__(self, resource, cycle_timestamp, violations, notifier_config, pipeline_config): super(EmailViolationsPipeline, self).__init__(resource, cycle_timestamp, violations, notifier_config, pipeline_config) self.mail_util = EmailUtil(self.pipeline_config['sendgrid_api_key']) def _get_output_filename(self): """Create the output filename. Returns: The output filename for the violations json. """ now_utc = datetime.utcnow() output_timestamp = now_utc.strftime(OUTPUT_TIMESTAMP_FMT) output_filename = VIOLATIONS_JSON_FMT.format(self.resource, self.cycle_timestamp, output_timestamp) return output_filename def _write_temp_attachment(self): """Write the attachment to a temp file. Returns: The output filename for the violations json just written. """ # Make attachment output_file_name = self._get_output_filename() output_file_path = '{}/{}'.format(TEMP_DIR, output_file_name) with open(output_file_path, 'w+') as f: f.write(parser.json_stringify(self.violations)) return output_file_name def _make_attachment(self): """Create the attachment object. Returns: The attachment object. """ output_file_name = self._write_temp_attachment() attachment = self.mail_util.create_attachment( file_location='{}/{}'.format(TEMP_DIR, output_file_name), content_type='text/json', filename=output_file_name, disposition='attachment', content_id='Violations' ) return attachment def _make_content(self): """Create the email content. Returns: A tuple containing the email subject and the content """ timestamp = datetime.strptime( self.cycle_timestamp, '%Y%m%dT%H%M%SZ') pretty_timestamp = timestamp.strftime("%d %B %Y - %H:%M:%S") email_content = self.mail_util.render_from_template( 'notification_summary.jinja', { 'scan_date': pretty_timestamp, 'resource': self.resource, 'violation_errors': self.violations, }) email_subject = 'Forseti Violations {} - {}'.format( pretty_timestamp, self.resource) return email_subject, email_content def _compose(self, **kwargs): """Compose the email pipeline map Returns: Returns a map with subject, content, attachemnt """ email_map = {} attachment = self._make_attachment() subject, content = self._make_content() email_map['subject'] = subject email_map['content'] = content email_map['attachment'] = attachment return email_map def _send(self, **kwargs): """Send a summary email of the scan. Args: subject: Email subject conetent: Email content attachment: Attachment object """ notification_map = kwargs.get('notification') subject = notification_map['subject'] content = notification_map['content'] attachment = notification_map['attachment'] self.mail_util.send(email_sender=self.pipeline_config['sender'], email_recipient=self.pipeline_config['recipient'], email_subject=subject, email_content=content, content_type='text/html', attachment=attachment) def run(self): """Run the email pipeline""" email_notification = self._compose() self._send(notification=email_notification)
class EmailViolationsPipeline(bnp.BaseNotificationPipeline): """Email pipeline to perform notifications""" def __init__(self, resource, cycle_timestamp, violations, global_configs, notifier_config, pipeline_config): """Initialization. Args: resource (str): Violation resource name. cycle_timestamp (str): Snapshot timestamp, formatted as YYYYMMDDTHHMMSSZ. violations (dict): Violations. global_configs (dict): Global configurations. notifier_config (dict): Notifier configurations. pipeline_config (dict): Pipeline configurations. """ super(EmailViolationsPipeline, self).__init__(resource, cycle_timestamp, violations, global_configs, notifier_config, pipeline_config) self.mail_util = EmailUtil(self.pipeline_config['sendgrid_api_key']) def _get_output_filename(self): """Create the output filename. Returns: str: The output filename for the violations json. """ now_utc = datetime.utcnow() output_timestamp = now_utc.strftime(OUTPUT_TIMESTAMP_FMT) output_filename = VIOLATIONS_JSON_FMT.format(self.resource, self.cycle_timestamp, output_timestamp) return output_filename def _write_temp_attachment(self): """Write the attachment to a temp file. Returns: str: The output filename for the violations json just written. """ # Make attachment output_file_name = self._get_output_filename() output_file_path = '{}/{}'.format(TEMP_DIR, output_file_name) with open(output_file_path, 'w+') as f: f.write(parser.json_stringify(self.violations)) return output_file_name def _make_attachment(self): """Create the attachment object. Returns: attachment: SendGrid attachment object. """ output_file_name = self._write_temp_attachment() attachment = self.mail_util.create_attachment( file_location='{}/{}'.format(TEMP_DIR, output_file_name), content_type='text/json', filename=output_file_name, disposition='attachment', content_id='Violations') return attachment def _make_content(self): """Create the email content. Returns: str: Email subject. unicode: Email template content rendered with the provided variables. """ timestamp = datetime.strptime(self.cycle_timestamp, '%Y%m%dT%H%M%SZ') pretty_timestamp = timestamp.strftime("%d %B %Y - %H:%M:%S") email_content = self.mail_util.render_from_template( 'notification_summary.jinja', { 'scan_date': pretty_timestamp, 'resource': self.resource, 'violation_errors': self.violations, }) email_subject = 'Forseti Violations {} - {}'.format( pretty_timestamp, self.resource) return email_subject, email_content def _compose(self, **kwargs): """Compose the email pipeline map Args: **kwargs: Arbitrary keyword arguments. Returns: dict: A map of the email with subject, content, attachemnt """ del kwargs email_map = {} attachment = self._make_attachment() subject, content = self._make_content() email_map['subject'] = subject email_map['content'] = content email_map['attachment'] = attachment return email_map def _send(self, **kwargs): """Send a summary email of the scan. Args: **kwargs: Arbitrary keyword arguments. subject: Email subject conetent: Email content attachment: Attachment object """ notification_map = kwargs.get('notification') subject = notification_map['subject'] content = notification_map['content'] attachment = notification_map['attachment'] try: self.mail_util.send( email_sender=self.pipeline_config['sender'], email_recipient=self.pipeline_config['recipient'], email_subject=subject, email_content=content, content_type='text/html', attachment=attachment) except util_errors.EmailSendError: LOGGER.warn('Unable to send Violations email') def run(self): """Run the email pipeline""" email_notification = self._compose() self._send(notification=email_notification)