def __init__(self, sendgrid_key): # pylint: disable=super-init-not-called """Initialize. Args: sendgrid_key (str): The SendGrid API key. """ self.email_util = EmailUtil(sendgrid_key)
def _send_email(cycle_timestamp, status, sendgrid_api_key, email_sender, email_recipient, email_content=None): """Send an email. Args: cycle_timestamp: String of timestamp, formatted as YYYYMMDDTHHMMSSZ. status: String of the current snapshot cycle. sendgrid_api_key: String of the sendgrid api key to auth email service. email_sender: String of the sender of the email. email_recipient: String of the recipient of the email. email_content: String of the email content (aka, body). Returns: None """ email_subject = 'Inventory loading {0}: {1}'.format(cycle_timestamp, status) if email_content is None: email_content = email_subject try: email_util = EmailUtil(sendgrid_api_key) email_util.send(email_sender, email_recipient, email_subject, email_content) except EmailSendError: LOGGER.error('Unable to send email that inventory snapshot completed.')
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 _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 _compose( # pylint: disable=arguments-differ self, snapshot_time, snapshot_timestamp, status, inventory_pipelines): """Compose the email content. Args: snapshot_time: Datetime object of the cycle, in UTC. snapshot_timestamp: String of timestamp, formatted as YYYYMMDDTHHMMSSZ. status: String of the overall status of current snapshot cycle. inventory_pipelines: List of inventory pipelines. Returns: email_subject: String of the email subject. email_content: String of template content rendered with the provided variables. """ email_subject = 'Inventory Snapshot Complete: {0} {1}'.format( snapshot_timestamp, status) email_content = EmailUtil.render_from_template( 'inventory_snapshot_summary.jinja', { 'snapshot_time': snapshot_time.strftime('%Y %b %d, %H:%M:%S (UTC)'), 'snapshot_timestamp': snapshot_timestamp, 'status_summary': status, 'pipelines': inventory_pipelines }) return email_subject, email_content
def _compose(self, snapshot_time, snapshot_timestamp, status, inventory_pipelines): """Compose the email content. Args: snapshot_time (datetime): Snapshot time, in UTC. snapshot_timestamp (str): Snapshot timestamp, formatted as YYYYMMDDTHHMMSSZ. status (str): Overall status of current snapshot cycle. inventory_pipelines (list): Inventory pipelines. Returns: string: Email subject. unicode: Email template content rendered with the provided variables. """ email_subject = 'Inventory Snapshot Complete: {0} {1}'.format( snapshot_timestamp, status) email_content = EmailUtil.render_from_template( 'inventory_snapshot_summary.jinja', { 'snapshot_time': snapshot_time.strftime('%Y %b %d, %H:%M:%S (UTC)'), 'snapshot_timestamp': snapshot_timestamp, 'status_summary': status, 'pipelines': inventory_pipelines }) return email_subject, email_content
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 _send_email(cycle_time, cycle_timestamp, status, pipelines, sendgrid_api_key, email_sender, email_recipient, email_content=None): """Send an email. Args: cycle_time: Datetime object of the cycle, in UTC. cycle_timestamp: String of timestamp, formatted as YYYYMMDDTHHMMSSZ. status: String of the overall status of current snapshot cycle. pipelines: List of pipelines and their statuses. sendgrid_api_key: String of the sendgrid api key to auth email service. email_sender: String of the sender of the email. email_recipient: String of the recipient of the email. email_content: String of the email content (aka, body). Returns: None """ email_subject = 'Inventory Snapshot Complete: {0} {1}'.format( cycle_timestamp, status) email_content = EmailUtil.render_from_template( 'inventory_snapshot_summary.jinja', { 'cycle_time': cycle_time.strftime('%Y %b %d, %H:%M:%S (UTC)'), 'cycle_timestamp': cycle_timestamp, 'status_summary': status, 'pipelines': pipelines, }) try: email_util = EmailUtil(sendgrid_api_key) email_util.send(email_sender, email_recipient, email_subject, email_content, content_type='text/html') except util_errors.EmailSendError: LOGGER.error('Unable to send email that inventory snapshot completed.')
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)
def __init__(self, sendgrid_key): # pylint: disable=super-init-not-called self.email_util = EmailUtil(sendgrid_key)
class EmailInventorySnapshopSummaryPipeline(bnp.BaseNotificationPipeline): """Email pipeline for inventory snapshot summary.""" # TODO: See if the base pipline init() can be reused. def __init__(self, sendgrid_key): # pylint: disable=super-init-not-called self.email_util = EmailUtil(sendgrid_key) def _compose( # pylint: disable=arguments-differ self, snapshot_time, snapshot_timestamp, status, inventory_pipelines): """Compose the email content. Args: snapshot_time: Datetime object of the cycle, in UTC. snapshot_timestamp: String of timestamp, formatted as YYYYMMDDTHHMMSSZ. status: String of the overall status of current snapshot cycle. inventory_pipelines: List of inventory pipelines. Returns: email_subject: String of the email subject. email_content: String of template content rendered with the provided variables. """ email_subject = 'Inventory Snapshot Complete: {0} {1}'.format( snapshot_timestamp, status) email_content = EmailUtil.render_from_template( 'inventory_snapshot_summary.jinja', { 'snapshot_time': snapshot_time.strftime('%Y %b %d, %H:%M:%S (UTC)'), 'snapshot_timestamp': snapshot_timestamp, 'status_summary': status, 'pipelines': inventory_pipelines }) return email_subject, email_content def _send( # pylint: disable=arguments-differ self, email_sender, email_recipient, email_subject, email_content, attachment=None): """Send a summary email of the scan. Args: email_sender: String of the sender of the email. email_recipient: String of the recipient of the email. email_subject: String of the email subject. email_content: String of template content rendered with the provided variables. """ try: self.email_util.send(email_sender=email_sender, email_recipient=email_recipient, email_subject=email_subject, email_content=email_content, content_type='text/html', attachment=attachment) except util_errors.EmailSendError: LOGGER.error('Unable to send email that inventory snapshot ' 'completed.') def run( # pylint: disable=arguments-differ self, snapshot_time, snapshot_timestamp, status, inventory_pipelines, email_sender, email_recipient): """Run the email pipeline Args: snapshot_time: Datetime object of the cycle, in UTC. snapshot_timestamp: String of timestamp, formatted as YYYYMMDDTHHMMSSZ. status: String of the overall status of current snapshot cycle. inventory_pipelines: List of inventory pipelines. email_sender: String of the sender of the email. email_recipient: String of the recipient of the email. Returns: None """ email_subject, email_content = self._compose(snapshot_time, snapshot_timestamp, status, inventory_pipelines) self._send(email_sender, email_recipient, email_subject, email_content)
class EmailScannerSummaryPipeline(bnp.BaseEmailNotificationPipeline): """Email pipeline for scanner summary.""" # TODO: See if the base pipline init() can be reused. def __init__(self, sendgrid_key): # pylint: disable=super-init-not-called """Initialize. Args: sendgrid_key (str): The SendGrid API key. """ self.email_util = EmailUtil(sendgrid_key) def _compose( # pylint: disable=arguments-differ self, all_violations, total_resources): """Compose the scan summary. Build a summary of the violations and counts for the email. resource summary: { RESOURCE_TYPE: { 'pluralized_resource_type': '{RESOURCE_TYPE}s' 'total': TOTAL, 'violations': { RESOURCE_ID: NUM_VIOLATIONS, RESOURCE_ID: NUM_VIOLATIONS, ... } }, ... } Args: all_violations (list): List of violations. total_resources (dict): A dict of the resources and their count. Returns: int: total_violations, an integer of the total violations. dict: resource_summaries, a dict of 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)])}} """ resource_summaries = {} total_violations = 0 for violation in sorted(all_violations, key=lambda v: v.get('resource_id')): resource_id = violation.get('resource_id') resource_type = violation.get('resource_type') if resource_type not in resource_summaries: resource_summaries[resource_type] = { 'pluralized_resource_type': resource_util.pluralize( resource_type), 'total': total_resources[resource_type], 'violations': collections.OrderedDict() } # Keep track of # of violations per resource id. if (resource_id not in resource_summaries[resource_type]['violations']): resource_summaries[resource_type]['violations'][resource_id] = 0 resource_summaries[resource_type]['violations'][resource_id] += 1 total_violations += 1 return total_violations, resource_summaries 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 run( # pylint: disable=arguments-differ self, csv_name, output_filename, now_utc, all_violations, total_resources, violation_errors, email_sender, email_recipient, email_description): """Run the email pipeline Args: csv_name (str): The full path of the local csv filename. output_filename (str): String of the output filename. now_utc (datetime): The UTC datetime right now. all_violations (list): The list of violations. total_resources (dict): A dict of the resources and their count. violation_errors (iterable): Iterable of violation errors. 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'. """ total_violations, resource_summaries = self._compose( all_violations, total_resources) self._send(csv_name, output_filename, now_utc, violation_errors, total_violations, resource_summaries, email_sender, email_recipient, email_description)
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)