def __init__(self): """ Load configuration and setup logging """ try: config = get_config(self.__class__.__name__) except Exception as e: config = None if config is not None: self._logger = get_logger(self.__class__.__name__, config.get_log_file(), config.get_file_verbosity(), config.get_screen_verbosity()) if config.has("SACCTMGR_PATH"): self._sacctmgr = config.get("SACCTMGR_PATH") else: self._logger.warning( "The configuration does not specify a path to sacctmgr via the key " + "SACCTMGR_PATH. Will use the default path /usr/bin/sacctmgr" ) self._sacctmgr = "/usr/bin/sacctmgr" if config.has("SACCT_SCHEMA"): self._schema = config.get("SACCT_SCHEMA") else: self._logger.warning( "The configuration does not specify a SLURM account schema via the key " + "SACCT_SCHEMA. Will use the default schema") self._schema = default_slurm_account_schema if config.has("DEFAULT_CLUSTER"): self._cluster = config.get("DEFAULT_CLUSTER") + ":" else: self._cluster = "" self._block_delete = False if config.has("BLOCK_DELETE"): self._block_delete = True else: print( "**************************************************************" ) print( "The configuration file metaroot[-test].yaml could not be found" ) print( "Using default parameters and logging at debug level to screen" ) print( "***************************************************************" ) self._logger = get_logger(self.__class__.__name__, "$NONE", "CRITICAL", "DEBUG") self._sacctmgr = "/usr/bin/sacctmgr" self._schema = default_slurm_account_schema self._cluster = "" self._block_delete = True
def test_event_producer_consumer_integration(self): st = Thread(target=run_server) st.start() config = get_config("EVENT_TEST") with Producer(config) as p: for i in range(10): result = p.send({ "action": "echo", "message": "hello {0}".format(i) }) self.assertEqual(0, result.status) result = p.send("CLOSE_IMMEDIATELY") self.assertEqual(0, result.status)
def test_rpc_client_server_integration(self): st = Thread(target=run_server) st.start() config = get_config("RPC_TEST") with RPCClient(config) as c: for i in range(10): message = "hello {0}".format(i) result = c.send({"action": "echo", "message": message}) self.assertEqual(0, result.status) self.assertEqual(message, result.response) result = c.send("CLOSE_IMMEDIATELY") self.assertEqual(0, result.status) self.assertEqual("SHUTDOWN_INIT", result.response) st.join()
def __init__(self): """ Initialize a new activity stream. Creates the database if it does not exist. """ config = get_config(self.__class__.__name__) db_file = config.get_activity_stream_db() need_to_create_tables = True if os.path.exists(db_file): need_to_create_tables = False # If the database does not exist, create it self._conn = sqlite3.connect(db_file) if need_to_create_tables: self._conn.execute( '''CREATE TABLE events (eventtime timestamp, type integer, action text, arguments text, status integer, message text)''' ) self._conn.commit()
def __init__(self): super().__init__(RPCClient(get_config(self.__class__.__name__)))
def __init__(self): super().__init__(Producer(get_config(self.__class__.__name__)))
def send_email(recipient_user_name: str, subject: str, body: str): """ Method to send an email notification with an HTML body Parameters ---------- recipient_user_name: str The user name that should receive the notification. This is resolved to an email address using either the builtin default resolver, or with a custom resolver specified in the SMTP configuration subject: str Subject of the email body: str Body of the email Returns ------- True If the email was sent False If the email could not be sent """ try: try: config = get_config("NOTIFICATIONS") except Exception as e: config = None if config is not None and config.has("SMTP_SERVER") and config.has( "SMTP_FROM"): # Resolve/validate the recipient email adress resolver = DefaultEmailAddressResolver() if config.has("SMTP_ADDRESS_RESOLVER"): resolver = instantiate_object_from_class_path( config.get("SMTP_ADDRESS_RESOLVER")) # # # # # The following is based heavily on https://stackoverflow.com/a/32129736/3357118 msg = email.message.Message() msg['Subject'] = subject msg['From'] = config.get("SMTP_FROM") msg['To'] = resolver.resolve_to_email_address(recipient_user_name) msg.add_header('Content-Type', 'text/html') msg.set_payload(body) s = smtplib.SMTP(config.get("SMTP_SERVER")) # Evaluate TLS configuration and start TLS is requested if config.has("SMTP_START_TLS") and config.get("SMTP_START_TLS"): s.starttls() else: logger.warning( "The value of SMTP_START_TLS is missing or did not evaluate to True, so not using TLS" ) # If a username and password were specified, authenticte to the SMPT server if config.has("SMTP_USER") and config.has("SMTP_PASSWORD"): logger.debug("Authenticating to the SMTP server") s.login(config.get("SMTP_USER"), config.get("SMTP_PASSWORD")) else: logger.debug("Not authenticating to the SMTP server") s.sendmail(msg['From'], [msg['To']], msg.as_string()) s.quit() # # # # # # return True else: logger.warning( "SMTP settings are missing on incomplete. Message \"%s\" to %s could not be sent", subject, recipient_user_name) return False except Exception as e: logger.exception(e) logger.error("Message \"%s\" to \"%s\" could not be sent", subject, recipient_user_name) return False
from metaroot.api.result import Result from metaroot.api.notifications import send_email from metaroot.config import get_config config = get_config("DEFAULTREACTIONS") class DefaultReactions: """ Reactions are defined to occur relative to the result of an action. In the standard deployment, they are applied by the router after each action. The default reaction is to send an email any time an error is generated. """ @staticmethod def occur_in_response_to(clazz: str, action: str, payload: object, result: Result, n_priors: int) -> int: """ Evaluates the result of an action and performs additional actions as necessary Parameters ---------- clazz: str The class name that implemented the method handling the action action: str The name of the method implemented by clazz that was called payload: object The argument that were passed to the method result: Result The result of the method call n_priors: int A value indicating the number of reactions that have occurred during the requested operation. I.e., this value is set to 0 as the router begins calling methods of each manager implementing the current request
def __init__(self): """ Initializes the ActivityStream and list of Managers that will be contacted when API requests arrive """ config = get_config(self.__class__.__name__) self._logger = get_logger(self.__class__.__name__, config.get_log_file(), config.get_file_verbosity(), config.get_screen_verbosity()) # Output all config parameters when debugging self._logger.debug("VVVVVV Router Config VVVVVV") debug_config(config) # Instantiate an activity stream to store a record of requests/responses if config.get_activity_stream() != "$NONE": self._logger.info("Logging activity using %s", config.get_activity_stream()) self.__activity_stream = instantiate_object_from_class_path( config.get_activity_stream()) else: self._logger.info("***Not recording an activity stream***") self.__activity_stream = NullActivityStream() # Initialize managers to receive requests hooks = config.get_hooks() self._managers = [] for hook in hooks: try: manager = instantiate_object_from_class_path(hook) try: # Managers must implement methods "initialize()" and "finalize()" to ensure clean statup/shutdown method = getattr(manager, "initialize") method() getattr(manager, "finalize") self._managers.append(manager) self._logger.info("Loaded manager for %s", hook) except AttributeError as e: self._logger.error( "Method 'initialize' or 'finalize' is not defined for manager/hook %s", manager.__class__.__name__) except Exception as e: self._logger.exception(e) self._logger.error("Exception while instantiating hook %s", hook) if len(self._managers) < len(hooks): self._logger.error( "%d of %d hooks were initialized. refusing to run with reduced set.", len(self._managers), len(hooks)) exit(1) # Initialize reactions to take actions relative to requests outcomes self._reactions = None if config.has("REACTION_HANDLER"): self._reactions = instantiate_object_from_class_path( config.get("REACTION_HANDLER")) else: self._logger.info( "No reaction handle specified so using DefaultReactions") from metaroot.api.reactions import DefaultReactions self._reactions = DefaultReactions() # Check for read-only operation to block write requests self._read_only = config.get_read_only_enabled()