def do_download_runtime(self, args): if args: try: mxversion = m2ee.version.MXVersion(args) except Exception: logger.error("The provided runtime version string is not a " "valid Mendix Runtime version number. Try using " "the format x.y.z, e.g. 4.7.1.") return else: mxversion = self.m2ee.config.get_runtime_version() if not mxversion: logger.info("You did not specify a Mendix Runtime version to " "download, and no current unpacked application " "model is available to determine the version from. " "Specify a version number or use unpack first.") return version = str(mxversion) if self.m2ee.config.lookup_in_mxjar_repo(version): logger.info("The Mendix Runtime for version %s is already " "installed. If you want to download another Runtime " "version, specify the version number as argument to " "download_runtime." % version) return if not self.m2ee.config.get_first_writable_mxjar_repo(): logger.error("None of the locations specified in the mxjar_repo " "configuration option are writable by the current " "user account.") return self.m2ee.download_and_unpack_runtime(version)
def do_check_health(self, args): if self._report_not_implemented('2.5.4') or self._report_not_running(): return health_response = self.m2ee.client.check_health() if not health_response.has_error(): feedback = health_response.get_feedback() if feedback['health'] == 'healthy': logger.info("Health check microflow says the application is " "healthy.") elif feedback['health'] == 'sick': logger.warning("Health check microflow says the application " "is sick: %s" % feedback['diagnosis']) elif feedback['health'] == 'unknown': logger.info("Health check microflow is not configured, no " "health information available.") else: logger.error("Unexpected health check status: %s" % feedback['health']) else: runtime_version = self.m2ee.config.get_runtime_version() if (health_response.get_result() == 3 and runtime_version // ('2.5.4', '2.5.5')): # 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. logger.info("Health check microflow is probably not " "configured, no health information available.") else: health_response.display_error()
def _write_http_output_config(http_config): logger.debug("writing http output config") if "url" not in http_config: logger.error("APPMETRICS_TARGET.url value is not defined in {}".format( _get_appmetrics_target())) return http_output = { "url": http_config["url"], "method": "POST", "data_format": "influx", } username = http_config.get("username") password = http_config.get("password") if username: # Workaround for https://github.com/influxdata/telegraf/issues/4544 # http_output['username'] = username # http_output['password'] = password credentials = base64.b64encode( ("{}:{}".format(username, password)).encode()).decode("ascii") http_output["[outputs.http.headers]"] = { "Authorization": "Basic {}".format(credentials) } kpionly = http_config["kpionly"] if "kpionly" in http_config else True if kpionly: http_output["[outputs.http.tagpass]"] = {"KPI": ["true"]} _write_config("[[outputs.http]]", http_output)
def parse_headers(): header_config = "" headers_from_json = {} # this is kept for X-Frame-Options backward compatibility x_frame_options = os.environ.get("X_FRAME_OPTIONS", "ALLOW") if x_frame_options != "ALLOW": headers_from_json["X-Frame-Options"] = x_frame_options headers_json = os.environ.get("HTTP_RESPONSE_HEADERS", "{}") try: headers_from_json.update(json.loads(headers_json)) except Exception as e: logger.error( "Failed to parse HTTP_RESPONSE_HEADERS, due to invalid JSON string: '{}'" .format(headers_json), exc_info=True, ) raise for header_key, header_value in headers_from_json.items(): regEx = DEFAULT_HEADERS[header_key] if regEx and re.match(regEx, header_value): escaped_value = header_value.replace('"', '\\"').replace("'", "\\'") header_config += "add_header {} '{}';\n".format( header_key, escaped_value) logger.debug("Added header {} to nginx config".format(header_key)) else: logger.warning( "Skipping {} config, value '{}' is not valid".format( header_key, header_value)) return header_config
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: # This is a dirty way to make it self-service until we pick up DEP-59. # After DEP-59 we can pick this up from a dedicated env var. agent_config = "" if "MetricsAgentConfig" in m2ee.config._conf["mxruntime"]: v = m2ee.config._conf["mxruntime"]["MetricsAgentConfig"] try: json.loads(v) # ensure that this contains valid json config_file_path = os.path.abspath( ".local/MetricsAgentConfig.json") with open(config_file_path, "w") as fh: fh.write(v) 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/mx-agent-assembly-0.1.1-SNAPSHOT.jar") m2ee.config._conf["m2ee"]["javaopts"].extend([ "-javaagent:{}{}".format(jar, agent_config), "-Xbootclasspath/a:{}".format(jar), ]) # if not explicitly set, default to statsd m2ee.config._conf["mxruntime"].setdefault("com.mendix.metrics.Type", "statsd")
def get_database_jdbc_url(self): """Return the database jdbc url for the M2EE configuration""" url = self.credentials.get("url", "") pattern = r"jdbc:sap://(?P<host>[^:]+):(?P<port>[0-9]+)/?(?P<q>\?(?P<params>.*))?$" match = re.search(pattern, url) if match is None: logger.error("Unable to parse Hana JDBC url string for parameters") raise Exception( "Unable to parse Hana JDBC url string for parameters" ) parameters = {} if match.group("q") is not None and match.group("params") is not None: q = match.group("q") params = match.group("params") parameters.update(parse_qs(params)) # override parameters from DATABASE_CONNECTION_PARAMS parameters.update(self.get_override_connection_parameters()) if q is not None and len(parameters) > 0: parameterStr = "?{}".format(urlencode(parameters, True)) url = url.replace(q, parameterStr) return url
def _write_http_output_config(http_config): logger.debug('writing http output config') if 'url' not in http_config: logger.error('APPMETRICS_TARGET.url value is not defined in {}'.format( _get_appmetrics_target())) return http_output = { 'url': http_config['url'], 'method': 'POST', 'data_format': 'influx' } username = http_config.get('username') password = http_config.get('password') if username: # Workaround for https://github.com/influxdata/telegraf/issues/4544 # http_output['username'] = username # http_output['password'] = password credentials = base64.b64encode( ('{}:{}'.format(username, password)).encode()).decode('ascii') http_output['[outputs.http.headers]'] = { 'Authorization': 'Basic {}'.format(credentials) } kpionly = http_config['kpionly'] if 'kpionly' in http_config else True if kpionly: http_output['[outputs.http.tagpass]'] = {'KPI': ['true']} _write_config('[[outputs.http]]', http_output)
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: # This is a dirty way to make it self-service until we pick up DEP-59. # After DEP-59 we can pick this up from a dedicated env var. agent_config = '' if 'MetricsAgentConfig' in m2ee.config._conf['mxruntime']: v = m2ee.config._conf['mxruntime']['MetricsAgentConfig'] try: json.loads(v) # ensure that this contains valid json config_file_path = os.path.abspath('.local/MetricsAgentConfig.json') with open(config_file_path, 'w') as fh: fh.write(v) 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/mx-agent-assembly-0.1.1-SNAPSHOT.jar') m2ee.config._conf['m2ee']['javaopts'].extend([ '-javaagent:{}{}'.format(jar, agent_config), '-Xbootclasspath/a:{}'.format(jar), ]) # if not explicitly set, default to statsd m2ee.config._conf['mxruntime'].setdefault( 'com.mendix.metrics.Type', 'statsd' )
def do_dumpdb(self, args): if not self.m2ee.config.is_using_postgresql(): logger.error("Only PostgreSQL databases are supported right now.") return if len(args) > 0: pgutil.dumpdb(self.m2ee.config, args) else: pgutil.dumpdb(self.m2ee.config)
def _start(self): """ This function deals with the start-up sequence of the Mendix Runtime. Starting the Mendix Runtime can fail in both a temporary or permanent way. See the client_errno for possible error codes. """ if not self.m2ee.config.get_runtime_path(): logger.error("It appears that the Mendix Runtime version which " "has to be used for your application is not present " "yet.") logger.info("You can try downloading it using the " "download_runtime command.") return if not self.m2ee.start_appcontainer(): return if not self.m2ee.send_runtime_config(): self._stop() return abort = False fully_started = False params = {} while not (fully_started or abort): startresponse = self.m2ee.start_runtime(params) result = startresponse.get_result() if result == client_errno.SUCCESS: fully_started = True else: startresponse.display_error() if result == client_errno.start_NO_EXISTING_DB: answer = self._ask_user_whether_to_create_db() if answer == 'a': abort = True elif (self.m2ee.config.get_runtime_version() // 2.5 and answer == 'c'): params["autocreatedb"] = True elif result == client_errno.start_INVALID_DB_STRUCTURE: answer = self._handle_ddl_commands() if answer == 'a': abort = True elif result == client_errno.start_MISSING_MF_CONSTANT: logger.error("You'll have to add the constant definitions " "to the configuration in the " "MicroflowConstants section.") abort = True elif result == client_errno.start_ADMIN_1: answer = self._handle_admin_1( startresponse.get_feedback()['users']) if answer == 'a': abort = True else: abort = True if abort: self._stop()
def set_user_provided_java_options(m2ee_section): javaopts = m2ee_section["javaopts"] options = os.environ.get("JAVA_OPTS", None) if options: try: options = json.loads(options) except json.JSONDecodeError as e: logger.error( "Failed to parse JAVA_OPTS, due to invalid JSON.", exc_info=True, ) raise javaopts.extend(options)
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 do_status(self, args): if self._report_not_running(): return feedback = self.m2ee.client.runtime_status().get_feedback() logger.info("The application process is running, the MxRuntime has " "status: %s" % feedback['status']) critlist = self.m2ee.client.get_critical_log_messages() if len(critlist) > 0: logger.error("%d critical error(s) were logged. Use show_critical" "_log_messages to view them." % len(critlist)) max_show_users = 10 total_users = self._who(max_show_users) if total_users > max_show_users: logger.info("Only showing %s logged in users. Use who to see a " "complete list." % max_show_users)
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" )
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)
def _report_not_implemented(self, avail_since): runtime_version = self.m2ee.config.get_runtime_version() if runtime_version is None: return False # DUNNO if not runtime_version >= avail_since: logger.error("This action is not available in the Mendix Runtime " "version you are currently using.") if isinstance(avail_since, tuple): if len(avail_since) > 2: implemented_in = ('%s, %s and %s' % (', '.join(map(str, avail_since[:-2])), avail_since[-2], avail_since[-1])) else: implemented_in = '%s and %s' % avail_since else: implemented_in = avail_since logger.error("It was implemented in Mendix %s" % implemented_in) return True return False
def do_unpack(self, args): if not args: logger.error("unpack needs the name of a model upload zipfile in " "%s as argument" % self.m2ee.config.get_model_upload_path()) return (pid_alive, m2ee_alive) = self.m2ee.check_alive() if pid_alive or m2ee_alive: logger.error("The application process is still running, refusing " "to unpack a new application model right now.") return logger.info("This command will replace the contents of the model/ and " "web/ locations, using the files extracted from the " "archive") answer = raw_input("Continue? (y)es, (N)o? ") if answer != 'y': logger.info("Aborting!") return self.m2ee.unpack(args)
def _report_not_implemented(self, avail_since): runtime_version = self.m2ee.config.get_runtime_version() if runtime_version is None: return False # DUNNO if not runtime_version >= avail_since: logger.error("This action is not available in the Mendix Runtime " "version you are currently using.") if isinstance(avail_since, tuple): if len(avail_since) > 2: implemented_in = ( '%s, %s and %s' % (', '.join(map(str, avail_since[:-2])), avail_since[-2], avail_since[-1])) else: implemented_in = '%s and %s' % avail_since else: implemented_in = avail_since logger.error("It was implemented in Mendix %s" % implemented_in) return True return False
def do_unpack(self, args): if not args: logger.error("unpack needs the name of a model upload zipfile in " "%s as argument" % self.m2ee.config.get_model_upload_path()) return (pid_alive, m2ee_alive) = self.m2ee.check_alive() if pid_alive or m2ee_alive: logger.error("The application process is still running, refusing " "to unpack a new application model right now.") return logger.info("This command will replace the contents of the model/ and " "web/ locations, using the files extracted from the " "archive") answer = ('y' if self.yolo_mode else raw_input("Continue? (y)es, (N)o? ")) if answer != 'y': logger.info("Aborting!") return self.m2ee.unpack(args)
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)
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)
def _ask_user_whether_to_create_db(self): answer = None while answer not in ('c', 'r', 'a'): if self.m2ee.config.get_dtap_mode()[0] in 'DT': answer = raw_input("Do you want to (c)reate, (r)etry, or " "(a)bort: ") else: answer = raw_input("Do you want to (r)etry, or (a)bort: ") if answer in ('a', 'r'): pass elif answer == 'c': if not self.m2ee.config.get_dtap_mode()[0] in ('D', 'T'): logger.error("Automatic Database creation is disabled in " "Acceptance and Production mode!") answer = None elif self.m2ee.config.get_runtime_version() >= 3: # If in Development/Test, call execute_ddl_commands, # because since 3.0, this tries to create a database and # immediately executes initial ddl commands m2eeresponse = self.m2ee.client.execute_ddl_commands() m2eeresponse.display_error() else: print("Unknown option %s" % answer) return answer
def do_interrupt_request(self, args): if (self._report_not_implemented(('2.5.8', 3.1)) or self._report_not_running()): return if args == "": logger.error("This function needs a request id as parameter") logger.error("Use show_current_runtime_requests to view currently " "running requests") return m2eeresp = self.m2ee.client.interrupt_request({"request_id": args}) m2eeresp.display_error() if not m2eeresp.has_error(): feedback = m2eeresp.get_feedback() if feedback["result"] is False: logger.error("A request with ID %s was not found" % args) else: logger.info("An attempt to cancel the running action was " "made.")
elif _signo == signal.SIGUSR2: emit(jvm={"ooms": 1.0}) else: # Should not happen pass m2ee.stop() sys.exit(1) signal.signal(signal.SIGTERM, sigterm_handler) signal.signal(signal.SIGUSR1, sigusr_handler) signal.signal(signal.SIGUSR2, sigusr_handler) try: service_backups() set_up_nginx_files(m2ee) telegraf.run() datadog.run() complete_start_procedure_safe_to_use_for_restart(m2ee) set_up_instadeploy_if_deploy_password_is_set(m2ee) start_metrics(m2ee) start_logging_heartbeat() start_nginx() loop_until_process_dies(m2ee) except Exception: x = traceback.format_exc() logger.error("Starting app container failed: %s" % x) callback_url = os.environ.get("BUILD_STATUS_CALLBACK_URL") if callback_url: requests.put(callback_url, x) raise
if os.getenv('CF_INSTANCE_INDEX') is None: logger.warning( 'CF_INSTANCE_INDEX environment variable not found. Assuming ' 'responsibility for scheduled events execution and database ' 'synchronization commands.') pre_process_m2ee_yaml() activate_license() m2ee = set_up_m2ee_client(get_vcap_data()) def sigterm_handler(_signo, _stack_frame): m2ee.stop() sys.exit(0) signal.signal(signal.SIGTERM, sigterm_handler) try: service_backups() set_up_nginx_files(m2ee) complete_start_procedure_safe_to_use_for_restart(m2ee) set_up_instadeploy_if_deploy_password_is_set(m2ee) start_metrics(m2ee) start_nginx() loop_until_process_dies(m2ee) except Exception: x = traceback.format_exc() logger.error('Starting app container failed: %s' % x) callback_url = os.environ.get('BUILD_STATUS_CALLBACK_URL') if callback_url: requests.put(callback_url, x) raise
def determine_cluster_redis_credentials(): vcap_services = buildpackutil.get_vcap_services_data() if vcap_services and 'rediscloud' in vcap_services: return vcap_services['rediscloud'][0]['credentials'] logger.error("Redis Cloud Service should be configured for this app") sys.exit(1)
def do_psql(self, args): if not self.m2ee.config.is_using_postgresql(): logger.error("Only PostgreSQL databases are supported right now.") return pgutil.psql(self.m2ee.config)
def _start(self): """ This function deals with the start-up sequence of the Mendix Runtime. Starting the Mendix Runtime can fail in both a temporary or permanent way. See the client_errno for possible error codes. """ if not self.m2ee.config.get_runtime_path(): logger.error("It appears that the Mendix Runtime version which " "has to be used for your application is not present " "yet.") logger.info("You can try downloading it using the " "download_runtime command.") return if not self.m2ee.start_appcontainer(): return database_password = None if not self.m2ee.config.has_database_password(): database_password = getpass.getpass( "Database password not configured, " "please provide now:") if not self.m2ee.send_runtime_config(database_password): self._stop() return abort = False fully_started = False params = {} while not (fully_started or abort): startresponse = self.m2ee.start_runtime(params) result = startresponse.get_result() if result == client_errno.SUCCESS: fully_started = True else: startresponse.display_error() if result == client_errno.start_NO_EXISTING_DB: answer = self._ask_user_whether_to_create_db() if answer == 'a': abort = True elif (self.m2ee.config.get_runtime_version() // 2.5 and answer == 'c'): params["autocreatedb"] = True elif result == client_errno.start_INVALID_DB_STRUCTURE: answer = self._handle_ddl_commands() if answer == 'a': abort = True elif result == client_errno.start_MISSING_MF_CONSTANT: logger.error("You'll have to add the constant definitions " "to the configuration in the " "MicroflowConstants section.") abort = True elif result == client_errno.start_ADMIN_1: users = startresponse.get_feedback()['users'] if self.yolo_mode: self._handle_admin_1_yolo(users) else: answer = self._handle_admin_1(users) if answer == 'a': abort = True else: abort = True if abort: self._stop()
def update_config(m2ee, app_name): if not is_enabled(): return tags = _get_tags() m2ee.config._conf['m2ee']['javaopts'].extend([ '-Dcom.sun.management.jmxremote', '-Dcom.sun.management.jmxremote.port=7845', '-Dcom.sun.management.jmxremote.local.only=true', '-Dcom.sun.management.jmxremote.authenticate=false', '-Dcom.sun.management.jmxremote.ssl=false', '-Djava.rmi.server.hostname=127.0.0.1', ]) if m2ee.config.get_runtime_version() >= 7.15: m2ee.config._conf['logging'].append({ 'type': 'tcpjsonlines', 'name': 'DataDogSubscriber', 'autosubscribe': 'INFO', 'host': 'localhost', 'port': 9032, }) if m2ee.config.get_runtime_version() >= 7.14: # This is a dirty way to make it self-service until we pick up DEP-59. # After DEP-59 we can pick this up from a dedicated env var. agent_config = '' if 'MetricsAgentConfig' in m2ee.config._conf['mxruntime']: v = m2ee.config._conf['mxruntime']['MetricsAgentConfig'] try: json.loads(v) # ensure that this contains valid json config_file_path = os.path.abspath( '.local/MetricsAgentConfig.json') with open(config_file_path, 'w') as fh: fh.write(v) 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/mx-agent-assembly-0.1.1-SNAPSHOT.jar') m2ee.config._conf['m2ee']['javaopts'].extend([ '-javaagent:{}{}'.format(jar, agent_config), '-Xbootclasspath/a:{}'.format(jar), ]) # if not explicitly set, default to statsd m2ee.config._conf['mxruntime'].setdefault('com.mendix.metrics.Type', 'statsd') subprocess.check_call(('mkdir', '-p', '.local/datadog')) with open('.local/datadog/datadog.yaml', 'w') as fh: config = { 'dd_url': 'https://app.datadoghq.com', 'api_key': None, # set via DD_API_KEY instead 'confd_path': '.local/datadog/conf.d', 'logs_enabled': True, 'log_file': '/dev/null', # will be printed via stdout/stderr 'hostname': _get_hostname(), 'tags': tags, 'process_config': { 'enabled': 'true', # has to be string 'log_file': '/dev/null', }, 'apm_config': { 'enabled': True, 'max_traces_per_second': 10, }, 'logs_config': { 'run_path': '.local/datadog/run', }, } fh.write(yaml.safe_dump(config)) subprocess.check_call(('mkdir', '-p', '.local/datadog/conf.d/mendix.d')) subprocess.check_call(('mkdir', '-p', '.local/datadog/run')) with open('.local/datadog/conf.d/mendix.d/conf.yaml', 'w') as fh: config = { 'logs': [{ 'type': 'tcp', 'port': '9032', 'service': _get_service(), 'source': 'mendix', 'tags': tags, }], } fh.write(yaml.safe_dump(config)) subprocess.check_call(('mkdir', '-p', '.local/datadog/conf.d/jmx.d')) with open('.local/datadog/conf.d/jmx.d/conf.yaml', 'w') as fh: # jmx beans and values can be inspected with jmxterm # download the jmxterm jar into the container # and run app/.local/bin/java -jar ~/jmxterm.jar # # the extra attributes are only available from Mendix 7.15.0+ config = { 'init_config': { 'collect_default_metrics': True, 'is_jmx': True, }, 'instances': [{ 'host': 'localhost', 'port': 7845, 'java_bin_path': '.local/bin/java', 'java_options': '-Xmx50m -Xms5m', # 'refresh_beans': 10, # runtime takes time to initialize the beans 'conf': [ { 'include': { 'bean': 'com.mendix:type=SessionInformation', # NamedUsers = 1; # NamedUserSessions = 0; # AnonymousSessions = 0; 'attribute': { 'NamedUsers': { 'metrics_type': 'gauge' }, 'NamedUserSessions': { 'metrics_type': 'gauge' }, 'AnonymousSessions': { 'metrics_type': 'gauge' }, }, }, }, { 'include': { 'bean': 'com.mendix:type=Statistics,name=DataStorage', # Selects = 1153; # Inserts = 1; # Updates = 24; # Deletes = 0; # Transactions = 25; 'attribute': { 'Selects': { 'metrics_type': 'counter' }, 'Updates': { 'metrics_type': 'counter' }, 'Inserts': { 'metrics_type': 'counter' }, 'Deletes': { 'metrics_type': 'counter' }, 'Transactions': { 'metrics_type': 'counter' }, }, }, }, { 'include': { 'bean': 'com.mendix:type=General', # Languages = en_US; # Entities = 24; 'attribute': { 'Entities': { 'metrics_type': 'gauge' }, }, }, }, { 'include': { 'bean': 'com.mendix:type=JettyThreadPool', # Threads = 8 # IdleThreads = 3; # IdleTimeout = 60000; # MaxThreads = 254; # StopTimeout = 30000; # MinThreads = 8; # ThreadsPriority = 5; # QueueSize = 0; 'attribute': { 'Threads': { 'metrics_type': 'gauge' }, 'MaxThreads': { 'metrics_type': 'gauge' }, 'IdleThreads': { 'metrics_type': 'gauge' }, 'QueueSize': { 'metrics_type': 'gauge' }, }, }, } ], # }, { # 'include': { # 'bean': 'com.mendix:type=Jetty', # # ConnectedEndPoints = 0; # # IdleTimeout = 30000; # # RequestsActiveMax = 0; # 'attribute': { # } # }, }], } fh.write(yaml.safe_dump(config)) _set_up_postgres()