Exemple #1
0
    def _get_db_conn(self):
        if self.db and self.db.closed != 0:
            self.db.close()
            self.db = None

        if not self.db:
            try:
                db_config = buildpackutil.get_database_config()
                if db_config['DatabaseType'] != 'PostgreSQL':
                    raise Exception(
                        'Metrics only supports postgresql, not %s'
                        % db_config['DatabaseType']
                    )
                host_and_port = db_config['DatabaseHost'].split(':')
                host = host_and_port[0]
                if len(host_and_port) > 1:
                    port = int(host_and_port[1])
                else:
                    port = 5432
                self.db = psycopg2.connect(
                    "options='-c statement_timeout=60s'",
                    database=db_config['DatabaseName'],
                    user=db_config['DatabaseUserName'],
                    password=db_config['DatabasePassword'],
                    host=host,
                    port=port,
                    connect_timeout=3,
                )
                self.db.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
            except Exception as e:
                logger.warn('METRICS: ' + e.message)
        return self.db
Exemple #2
0
 def _get_database_index_size(self):
     conn = self._get_db_conn()
     try:
         with conn.cursor() as cursor:
             cursor.execute(
                 """
                 SELECT
                     SUM(pg_relation_size(quote_ident(indexrelname)::text)) AS index_size
                 FROM pg_tables t
                 LEFT OUTER JOIN pg_class c ON t.tablename=c.relname
                 LEFT OUTER JOIN
                     ( SELECT c.relname AS ctablename, ipg.relname AS indexname, x.indnatts AS number_of_columns, idx_scan, idx_tup_read, idx_tup_fetch, indexrelname, indisunique FROM pg_index x
                         JOIN pg_class c ON c.oid = x.indrelid
                         JOIN pg_class ipg ON ipg.oid = x.indexrelid
                         JOIN pg_stat_all_indexes psai ON x.indexrelid = psai.indexrelid )
                     AS foo
                     ON t.tablename = foo.ctablename
                 WHERE t.schemaname='public';
                 """
             )
             rows = cursor.fetchall()
             return int(rows[0][0])
     except Exception as e:
         logger.warn(
             'Metrics: Failed to get database index size, ' + str(e)
         )
     return None
def run():
    if not is_enabled():
        return

    if not _is_installed():
        logger.warn(
            "DataDog agent isn"
            "t installed yet but DD_API_KEY is set. " +
            "Please push or restage your app to complete DataDog installation."
        )
        return

    e = dict(os.environ)
    e["DD_HOSTNAME"] = buildpackutil.get_hostname()
    e["DD_API_KEY"] = get_api_key()
    e["LD_LIBRARY_PATH"] = os.path.abspath(".local/datadog/lib/")
    subprocess.Popen(
        (".local/datadog/datadog-agent", "-c", ".local/datadog", "start"),
        env=e,
    )
    # after datadog agent 6.3 is released, a separate process agent might
    # not be necessary any more: https://github.com/DataDog/datadog-process-agent/pull/124
    subprocess.Popen(
        (
            ".local/datadog/process-agent",
            "-logtostderr",
            "-config",
            ".local/datadog/datadog.yaml",
        ),
        env=e,
    )
 def _get_database_mutations(self):
     conn = self._get_db_conn()
     try:
         db_config = buildpackutil.get_database_config()
         with conn.cursor() as cursor:
             cursor.execute("SELECT xact_commit, "
                            "       xact_rollback, "
                            "       tup_inserted, "
                            "       tup_updated, "
                            "       tup_deleted "
                            "FROM pg_stat_database "
                            "WHERE datname = '%s';" %
                            (db_config['DatabaseName'], ))
             rows = cursor.fetchall()
             return {
                 'xact_commit': int(rows[0][0]),
                 'xact_rollback': int(rows[0][1]),
                 'tup_inserted': int(rows[0][2]),
                 'tup_updated': int(rows[0][3]),
                 'tup_deleted': int(rows[0][4]),
             }
     except Exception as e:
         logger.warn('Metrics: Failed to get database mutation stats, ' +
                     str(e))
     return None
def _set_up_postgres():
    # TODO: set up a way to disable this, on shared database (mxapps.io) we
    # don't want to allow this.
    if not buildpackutil.i_am_primary_instance():
        return
    dbconfig = database_config.get_database_config()
    for k in (
        "DatabaseType",
        "DatabaseUserName",
        "DatabasePassword",
        "DatabaseHost",
    ):
        if k not in dbconfig:
            logger.warn(
                "Skipping database configuration for DataDog because "
                "configuration is not found. See database_config.py "
                "for details"
            )
            return
    if dbconfig["DatabaseType"] != "PostgreSQL":
        return
    with open(".local/datadog/conf.d/postgres.yaml", "w") as fh:
        config = {
            "init_config": {},
            "instances": [
                {
                    "host": dbconfig["DatabaseHost"].split(":")[0],
                    "port": int(dbconfig["DatabaseHost"].split(":")[1]),
                    "username": dbconfig["DatabaseUserName"],
                    "password": dbconfig["DatabasePassword"],
                    "dbname": dbconfig["DatabaseName"],
                }
            ],
        }
        fh.write(yaml.safe_dump(config))
 def _inject_storage_stats(self, stats):
     storage_stats = {}
     try:
         storage_stats['get_number_of_files'] = self._get_number_of_files()
     except Exception as e:
         logger.warn('Metrics: Failed to retrieve number of files, ' +
                     str(e))
     stats["storage"] = storage_stats
     return stats
Exemple #7
0
def _get_tags():
    # Telegraf tags must be key / value
    tags = {}
    for kv in [t.split(":") for t in buildpackutil.get_tags()]:
        if len(kv) == 2:
            tags[kv[0]] = kv[1]
        else:
            logger.warn(
                'Skipping tag "{}" from TAGS because not a key/value'.format(
                    kv[0]))
    return tags
 def _get_database_table_size(self):
     conn = self._get_db_conn()
     try:
         db_config = buildpackutil.get_database_config()
         with conn.cursor() as cursor:
             cursor.execute("SELECT pg_database_size('%s');" %
                            (db_config['DatabaseName'], ))
             rows = cursor.fetchall()
             return int(rows[0][0])
     except Exception as e:
         logger.warn('Metrics: Failed to get database data size, ' + str(e))
     return None
Exemple #9
0
 def do_who(self, args):
     if self._report_not_running():
         return
     if args:
         try:
             limitint = int(args)
             self._who(limitint)
         except ValueError:
             logger.warn("Could not parse argument to an integer. Use a "
                         "number as argument to limit the amount of logged "
                         "in users shown.")
     else:
         self._who()
Exemple #10
0
 def do_who(self, args):
     if self._report_not_running():
         return
     if args:
         try:
             limitint = int(args)
             self._who(limitint)
         except ValueError:
             logger.warn("Could not parse argument to an integer. Use a "
                         "number as argument to limit the amount of logged "
                         "in users shown.")
     else:
         self._who()
def run():
    if not is_enabled():
        return

    if not _is_installed():
        logger.warn(
            'Telegraf isn\'t installed yet but APPMETRICS_TARGET is set. ' +
            'Please push or restage your app to complete Telegraf installation.'
        )
        return

    e = dict(os.environ)
    subprocess.Popen(('.local/telegraf/usr/bin/telegraf', '--config',
                      '.local/telegraf/etc/telegraf/telegraf.conf'),
                     env=e)
Exemple #12
0
 def do_create_admin_user(self, args=None):
     (pid_alive, m2ee_alive) = self.m2ee.check_alive()
     if not m2ee_alive:
         logger.warn("The application process needs to be running to "
                     "create a user object in the application.")
         return
     print("This option will create an administrative user account, using "
           "the preset username and user role settings.")
     newpw1 = getpass.getpass("Type new password for this user: "******"Type new password for this user again: ")
     if newpw1 != newpw2:
         print("The passwords are not equal!")
     else:
         m2eeresponse = self.m2ee.client.create_admin_user(
             {"password": newpw1})
         m2eeresponse.display_error()
Exemple #13
0
 def do_create_admin_user(self, args=None):
     (pid_alive, m2ee_alive) = self.m2ee.check_alive()
     if not m2ee_alive:
         logger.warn("The application process needs to be running to "
                     "create a user object in the application.")
         return
     print("This option will create an administrative user account, using "
           "the preset username and user role settings.")
     newpw1 = getpass.getpass("Type new password for this user: "******"Type new password for this user again: ")
     if newpw1 != newpw2:
         print("The passwords are not equal!")
     else:
         m2eeresponse = self.m2ee.client.create_admin_user(
             {"password": newpw1})
         m2eeresponse.display_error()
Exemple #14
0
 def do_emptydb(self, args):
     if not self.m2ee.config.is_using_postgresql():
         logger.error("Only PostgreSQL databases are supported right now.")
         return
     (pid_alive, m2ee_alive) = self.m2ee.check_alive()
     if pid_alive or m2ee_alive:
         logger.warn("The application process is still running, refusing "
                     "to empty the database right now.")
         return
     logger.info("This command will drop all tables and sequences in "
                 "database %s." %
                 self.m2ee.config.get_pg_environment()['PGDATABASE'])
     answer = raw_input("Continue? (y)es, (N)o? ")
     if answer != 'y':
         print("Aborting!")
         return
     pgutil.emptydb(self.m2ee.config)
    def _inject_m2ee_stats(self, stats):
        try:
            m2ee_stats, java_version = munin.get_stats_from_runtime(
                self.m2ee.client, self.m2ee.config)
            if 'sessions' in m2ee_stats:
                m2ee_stats['sessions']['user_sessions'] = {}
            m2ee_stats = munin.augment_and_fix_stats(
                m2ee_stats, self.m2ee.runner.get_pid(), java_version)

            critical_logs_count = len(
                self.m2ee.client.get_critical_log_messages())
            m2ee_stats['critical_logs_count'] = critical_logs_count
            stats['mendix_runtime'] = m2ee_stats
        except Exception as e:
            logger.warn('Metrics: Failed to get Mendix Runtime metrics, ' +
                        str(e))
        return stats
def enable_runtime_agent(m2ee):
    # check already configured
    if 0 in [
        v.find("-javaagent") for v in m2ee.config._conf["m2ee"]["javaopts"]
    ]:
        return

    if m2ee.config.get_runtime_version() >= 7.14:
        agent_config = ""
        agent_config_str = None

        if "METRICS_AGENT_CONFIG" in os.environ:
            agent_config_str = os.environ.get("METRICS_AGENT_CONFIG")
        elif "MetricsAgentConfig" in m2ee.config._conf["mxruntime"]:
            logger.warn(
                "Passing MetricsAgentConfig with Mendix Custom Runtime Setting is deprecated. "
                + "Please use METRICS_AGENT_CONFIG as environment variable."
            )
            agent_config_str = m2ee.config._conf["mxruntime"][
                "MetricsAgentConfig"
            ]

        if agent_config_str:
            try:
                # ensure that this contains valid json
                json.loads(agent_config_str)
                config_file_path = os.path.abspath(
                    ".local/MetricsAgentConfig.json"
                )
                with open(config_file_path, "w") as fh:
                    fh.write(agent_config_str)
                agent_config = "=config=" + config_file_path
            except ValueError:
                logger.error(
                    "Could not parse json from MetricsAgentConfig",
                    exc_info=True,
                )
        jar = os.path.abspath(".local/datadog/{}".format(MX_AGENT_JAR))
        m2ee.config._conf["m2ee"]["javaopts"].extend(
            ["-javaagent:{}{}".format(jar, agent_config)]
        )
        # if not explicitly set, default to statsd
        m2ee.config._conf["mxruntime"].setdefault(
            "com.mendix.metrics.Type", "statsd"
        )
Exemple #17
0
 def do_log(self, args):
     if self._cleanup_logging():
         return
     logfile = self.m2ee.config.get_logfile()
     if not logfile:
         logger.warn("logfile location is not specified")
         return
     print("This command will start printing log information from the "
           "application right in the middle of all of the other output on "
           "your screen. This can be confusing, especially when you're "
           "typing something and everything gets messed up by the logging. "
           "Issuing the log command again will turn off logging output.")
     answer = raw_input("Do you want to start log output (y/N): ")
     if answer == 'y':
         cmd = ("tail", "-F", logfile)
         proc = subprocess.Popen(cmd)
         self.m2ee._logproc = proc
         self.prompt = "LOG %s" % self._default_prompt
Exemple #18
0
 def do_update_admin_user(self, args=None):
     (pid_alive, m2ee_alive) = self.m2ee.check_alive()
     if not m2ee_alive:
         logger.warn("The application process needs to be running to "
                     "change user objects in the application.")
         return
     print("Using this function you can reset the password of an "
           "administrative user account.")
     username = raw_input("User name: ")
     newpw1 = getpass.getpass("Type new password for user %s: " % username)
     newpw2 = getpass.getpass("Type new password for user %s again: " %
                              username)
     if newpw1 != newpw2:
         print("The passwords are not equal!")
     else:
         m2eeresponse = self.m2ee.client.update_admin_user(
             {"username": username, "password": newpw1})
         m2eeresponse.display_error()
Exemple #19
0
 def do_emptydb(self, args):
     if not self.m2ee.config.is_using_postgresql():
         logger.error("Only PostgreSQL databases are supported right now.")
         return
     (pid_alive, m2ee_alive) = self.m2ee.check_alive()
     if pid_alive or m2ee_alive:
         logger.warn("The application process is still running, refusing "
                     "to empty the database right now.")
         return
     logger.info("This command will drop all tables and sequences in "
                 "database %s." %
                 self.m2ee.config.get_pg_environment()['PGDATABASE'])
     answer = ('y'
               if self.yolo_mode else raw_input("Continue? (y)es, (N)o? "))
     if answer != 'y':
         print("Aborting!")
         return
     pgutil.emptydb(self.m2ee.config)
Exemple #20
0
 def do_log(self, args):
     if self._cleanup_logging():
         return
     logfile = self.m2ee.config.get_logfile()
     if not logfile:
         logger.warn("logfile location is not specified")
         return
     print("This command will start printing log information from the "
           "application right in the middle of all of the other output on "
           "your screen. This can be confusing, especially when you're "
           "typing something and everything gets messed up by the logging. "
           "Issuing the log command again will turn off logging output.")
     answer = ('y' if self.yolo_mode else
               raw_input("Do you want to start log output (y/N): "))
     if answer == 'y':
         cmd = ("tail", "-F", logfile)
         proc = subprocess.Popen(cmd)
         self.m2ee._logproc = proc
         self.prompt = "LOG %s" % self._default_prompt
Exemple #21
0
 def do_restoredb(self, args):
     if not self.m2ee.config.is_using_postgresql():
         logger.error("Only PostgreSQL databases are supported right now.")
         return
     if not args:
         logger.error("restoredb needs the name of a dump file in %s as arg"
                      "ument" % self.m2ee.config.get_database_dump_path())
         return
     (pid_alive, m2ee_alive) = self.m2ee.check_alive()
     if pid_alive or m2ee_alive:
         logger.warn("The application is still running, refusing to "
                     "restore the database right now.")
         return
     database_name = self.m2ee.config.get_pg_environment()['PGDATABASE']
     answer = raw_input("This command will restore this dump into database "
                        "%s. Continue? (y)es, (N)o? " % database_name)
     if answer != 'y':
         logger.info("Aborting!")
         return
     pgutil.restoredb(self.m2ee.config, args)
Exemple #22
0
def run():
    if not is_enabled():
        return

    if not _is_installed():
        logger.warn(
            "Telegraf isn't installed yet but APPMETRICS_TARGET is set. " +
            "Please push or restage your app to complete Telegraf installation."
        )
        return

    e = dict(os.environ)
    subprocess.Popen(
        (
            ".local/telegraf/usr/bin/telegraf",
            "--config",
            ".local/telegraf/etc/telegraf/telegraf.conf",
        ),
        env=e,
    )
Exemple #23
0
 def do_restoredb(self, args):
     if not self.m2ee.config.is_using_postgresql():
         logger.error("Only PostgreSQL databases are supported right now.")
         return
     if not args:
         logger.error("restoredb needs the name of a dump file in %s as arg"
                      "ument" % self.m2ee.config.get_database_dump_path())
         return
     (pid_alive, m2ee_alive) = self.m2ee.check_alive()
     if pid_alive or m2ee_alive:
         logger.warn("The application is still running, refusing to "
                     "restore the database right now.")
         return
     database_name = self.m2ee.config.get_pg_environment()['PGDATABASE']
     answer = ('y' if self.yolo_mode else raw_input(
         "This command will restore this dump into database "
         "%s. Continue? (y)es, (N)o? " % database_name))
     if answer != 'y':
         logger.info("Aborting!")
         return
     pgutil.restoredb(self.m2ee.config, args)
Exemple #24
0
 def do_update_admin_user(self, args=None):
     (pid_alive, m2ee_alive) = self.m2ee.check_alive()
     if not m2ee_alive:
         logger.warn("The application process needs to be running to "
                     "change user objects in the application.")
         return
     print("Using this function you can reset the password of an "
           "administrative user account.")
     username = raw_input("User name: ")
     newpw1 = getpass.getpass("Type new password for user %s: " % username)
     newpw2 = getpass.getpass("Type new password for user %s again: " %
                              username)
     if newpw1 != newpw2:
         print("The passwords are not equal!")
     else:
         m2eeresponse = self.m2ee.client.update_admin_user({
             "username":
             username,
             "password":
             newpw1
         })
         m2eeresponse.display_error()
Exemple #25
0
    def _inject_health(self, stats):
        health = {}
        translation = {'healthy': 10,
                       'unknown': 7,
                       'sick': 3,
                       'critical': 0}
        stats['health'] = health

        try:
            health_response = self.m2ee.client.check_health()
            if health_response.has_error():
                if (health_response.get_result() == 3 and
                        health_response.get_cause() == "java.lang.IllegalArgument"
                        "Exception: Action should not be null"):
                    # Because of an incomplete implementation, in Mendix 2.5.4 or
                    # 2.5.5 this means that the runtime is health-check
                    # capable, but no health check microflow is defined.
                    health['health'] = translation['unknown']
                    health['diagnosis'] = "No health check microflow defined"
                elif (health_response.get_result() ==
                        health_response.ERR_ACTION_NOT_FOUND):
                    # Admin action 'check_health' does not exist.
                    health['health'] = translation['unknown']
                    health['diagnosis'] = "No health check microflow defined"
                else:
                    health['health'] = translation['critical']
                    health['diagnosis'] = "Health check failed unexpectedly: %s" \
                                          % health_response.get_error()
            else:
                feedback = health_response.get_feedback()
                health['health'] = translation[feedback['health']]
                health['diagnosis'] = feedback['diagnosis'] if 'diagnosis' in feedback else ''
                health['response'] = health_response._json
        except Exception as e:
            logger.warn('Metrics: Failed to get health status, ' + str(e))
            health['health'] = translation['critical']
            health['diagnosis'] = "Health check failed unexpectedly: %s" % e

        return stats
def run():
    if not is_enabled():
        return

    if not _is_installed():
        logger.warn('DataDog agent isn''t installed yet but DD_API_KEY is set. ' +
                    'Please push or restage your app to complete DataDog installation.')
        return

    e = dict(os.environ)
    e['DD_HOSTNAME'] = buildpackutil.get_hostname()
    e['DD_API_KEY'] = get_api_key()
    e['LD_LIBRARY_PATH'] = os.path.abspath('.local/datadog/lib/')
    subprocess.Popen((
        '.local/datadog/datadog-agent', '-c', '.local/datadog', 'start',
    ), env=e)
    # after datadog agent 6.3 is released, a separate process agent might
    # not be necessary any more: https://github.com/DataDog/datadog-process-agent/pull/124
    subprocess.Popen((
        '.local/datadog/process-agent', '-logtostderr',
        '-config', '.local/datadog/datadog.yaml',
    ), env=e)
    def _inject_health(self, stats):
        health = {}
        translation = {"healthy": 10, "unknown": 7, "sick": 4, "critical": 0}
        stats["health"] = health

        try:
            health_response = self.m2ee.client.check_health()
            if health_response.has_error():
                if (health_response.get_result() == 3
                        and health_response.get_cause()
                        == "java.lang.IllegalArgument"
                        "Exception: Action should not be null"):
                    # Because of an incomplete implementation, in Mendix 2.5.4 or
                    # 2.5.5 this means that the runtime is health-check
                    # capable, but no health check microflow is defined.
                    health["health"] = translation["unknown"]
                    health["diagnosis"] = "No health check microflow defined"
                elif (health_response.get_result() ==
                      health_response.ERR_ACTION_NOT_FOUND):
                    # Admin action 'check_health' does not exist.
                    health["health"] = translation["unknown"]
                    health["diagnosis"] = "No health check microflow defined"
                else:
                    health["health"] = translation["critical"]
                    health["diagnosis"] = (
                        "Health check failed unexpectedly: %s" %
                        health_response.get_error())
            else:
                feedback = health_response.get_feedback()
                health["health"] = translation[feedback["health"]]
                health["diagnosis"] = (feedback["diagnosis"]
                                       if "diagnosis" in feedback else "")
                health["response"] = health_response._json
        except Exception as e:
            logger.warn("Metrics: Failed to get health status, " + str(e))
            health["health"] = translation["critical"]
            health["diagnosis"] = "Health check failed unexpectedly: %s" % e
        return stats