diff --git a/CONTAINER_SETUP.sh b/CONTAINER_SETUP.sh new file mode 100644 index 0000000000..6e255f54bd --- /dev/null +++ b/CONTAINER_SETUP.sh @@ -0,0 +1,230 @@ +#!/bin/bash +# This SETUP Script should ONLY BE USED WHEN running inside of an container like for example Docker + +if ! [ $(id -u) = 0 ]; then + echo "You need to run this script as root user or via sudo!" + exit 1 +fi + +# Enable debug mode so that CakePHP will create missing folders +# https://github.com/it-novum/openITCOCKPIT/issues/1446 +# https://github.com/cakephp/migrations/issues/565 +export OITC_DEBUG=1 + +APPDIR="/opt/openitc/frontend" + +INIFILE=/opt/openitc/etc/mysql/mysql.cnf +DUMPINIFILE=/opt/openitc/etc/mysql/dump.cnf +BASHCONF=/opt/openitc/etc/mysql/bash.conf + +OSVERSION=$(grep VERSION_CODENAME /etc/os-release | cut -d= -f2) +OS_BASE="debian" + +echo "Copy required system files" +rsync -K -a ${APPDIR}/system/etc/. /etc/ # we use rsync because the destination can be a symlink on RHEL +chown root:root /etc +cp -r ${APPDIR}/system/usr/. /usr/ +cp ${APPDIR}/system/nginx/ssl_options_$OSVERSION /etc/nginx/openitc/ssl_options.conf +cp ${APPDIR}/system/nginx/ssl_cert.conf /etc/nginx/openitc/ssl_cert.conf +echo "# This file will NOT be overwritten during an update" >> /etc/nginx/openitc/custom.conf +chmod +x /usr/bin/oitc + +echo "Create required system folders" +mkdir -p /opt/openitc/etc/{mysql,grafana,carbon,frontend,nagios,nsta} +mkdir -p /opt/openitc/nagios/etc/config +mkdir -p /opt/openitc/etc/nagios/nagios.cfg.d + +chown www-data:www-data /opt/openitc/logs/frontend +chown nagios:www-data /opt/openitc/nagios/etc/config +chown nagios:www-data /opt/openitc/etc/nagios/nagios.cfg.d +chmod 775 /opt/openitc/logs/frontend + +chown www-data:www-data /opt/openitc/frontend/tmp + +mkdir -p /opt/openitc/frontend/webroot/img/charts +chown www-data:www-data /opt/openitc/frontend/webroot/img/charts + +if [[ -d /opt/openitc/frontend/plugins/MapModule/webroot/img/ ]]; then + chown -R www-data:www-data /opt/openitc/frontend/plugins/MapModule/webroot/img/ +fi + +# It is important that the statusengine-worker container is already up and running and has created all +# the Statusengine related MySQL tables, because the setup will add partitions to the tables +echo "Checking if Statusengine Tables where already created..." +COUNTER=0 +MYSQL_ONLINE=0 + +set +e +while [ "$COUNTER" -lt 30 ]; do + #Is Grafana Server Online? + OUTPUT=$(mysql "--defaults-extra-file=$INIFILE" -e "SELECT 1 FROM statusengine_nodes;" -B -s 2>/dev/null) + + if [ "$OUTPUT" ]; then + echo "Tables are present." + MYSQL_ONLINE=1 + break + fi + echo "Waiting for Statusengine to create tables..." + COUNTER=$((COUNTER + 1)) + sleep 1 +done + +if [[ "$MYSQL_ONLINE" == 0 ]]; then + echo "ERROR!" + echo "Statusengine tables are missing! ABORT!" + exit 1 +fi +set -e + +#mkdir -p /opt/openitc/var/prometheus +#chown nagios:nagios /opt/openitc/var/prometheus +#mkdir -p /opt/openitc/var/prometheus/victoria-metrics + +echo "---------------------------------------------------------------" +echo "Import openITCOCKPIT Core database schema" +oitc migrations migrate + + +echo "---------------------------------------------------------------" +echo "Load default database" +oitc migrations seed + +echo "Running openITCOCKPIT Module database migration/s" +for PLUGIN in $(ls -1 "${APPDIR}/plugins"); do + if [[ "$PLUGIN" == *Module ]]; then + if [[ -d "${APPDIR}/plugins/${PLUGIN}/config/Migrations" ]]; then + echo "Running openITCOCKPIT ${PLUGIN} database migration" + oitc migrations migrate -p "${PLUGIN}" + fi + + if [[ -d "${APPDIR}/plugins/${PLUGIN}/config/Seeds" ]]; then + num_files=$(find "${APPDIR}/plugins/${PLUGIN}/config/Seeds" -mindepth 1 -iname "*.php" -type f | wc -l) + if [[ "$num_files" -gt 0 ]]; then + echo "Importing default records for ${PLUGIN} into database" + oitc migrations seed -p "${PLUGIN}" + fi + fi + + fi +done + +if [ -d "${APPDIR}/plugins/CheckmkModule/src" ]; then + oitc checkmkNagiosExport --init +fi + +echo "---------------------------------------------------------------" +echo "Create new WebSocket Key" +WEBSOCKET_KEY=$(php -r "echo bin2hex(openssl_random_pseudo_bytes(80, \$cstrong));") +mysql "--defaults-extra-file=${INIFILE}" -e "UPDATE systemsettings SET \`systemsettings\`.\`value\`='${WEBSOCKET_KEY}' WHERE \`key\`='SUDO_SERVER.API_KEY';" + +echo $OITC_GRAFANA_ADMIN_PASSWORD > /opt/openitc/etc/grafana/admin_password + +oitc config_generator_shell --generate-container + +echo "---------------------------------------------------------------" +echo "Configure Grafana" + +if [ ! -f /opt/openitc/etc/grafana/api_key ]; then + echo "Create new Grafana API Key for openITCOCKPIT" + COUNTER=0 + + set +e + while [ "$COUNTER" -lt 30 ]; do + echo "Try to connect to Grafana API..." + #Is Grafana Server Online? + STATUSCODE=$(curl --noproxy "$OITC_GRAFANA_HOSTNAME" "$OITC_GRAFANA_URL/api/admin/stats" -XGET -uadmin:$OITC_GRAFANA_ADMIN_PASSWORD -H 'Content-Type: application/json' -I 2>/dev/null | head -n 1 | cut -d$' ' -f2) + + if [ "$STATUSCODE" == "200" ]; then + API_KEY=$(curl --noproxy "$OITC_GRAFANA_HOSTNAME" "$OITC_GRAFANA_URL/api/auth/keys" -XPOST -uadmin:$OITC_GRAFANA_ADMIN_PASSWORD -H 'Content-Type: application/json' -d '{"role":"Editor","name":"openITCOCKPIT"}' | jq -r '.key') + echo "$API_KEY" >/opt/openitc/etc/grafana/api_key + break + fi + COUNTER=$((COUNTER + 1)) + sleep 1 + done + + if [ ! -f /opt/openitc/etc/grafana/api_key ]; then + echo "ERROR!" + echo "Could not create API key for Grafana" + fi + set -e +fi + +echo "Check if Graphite Datasource exists in Grafana" +DS_STATUSCODE=$(curl --noproxy "$OITC_GRAFANA_HOSTNAME" "$OITC_GRAFANA_URL/api/datasources/name/Graphite" -XGET -uadmin:$OITC_GRAFANA_ADMIN_PASSWORD -H 'Content-Type: application/json' -I 2>/dev/null | head -n 1 | cut -d$' ' -f2) +if [ "$DS_STATUSCODE" == "404" ]; then + echo "Create Graphite as default Datasource for Grafana" + RESPONSE=$(curl --noproxy "$OITC_GRAFANA_HOSTNAME" "$OITC_GRAFANA_URL/api/datasources" -XPOST -uadmin:$OITC_GRAFANA_ADMIN_PASSWORD -H 'Content-Type: application/json' -d '{ + "name":"Graphite", + "type":"graphite", + "url":"http://graphite-web:8080", + "access":"proxy", + "basicAuth":false, + "isDefault": true, + "jsonData": { + "graphiteVersion": 1.1 + } + }') + echo $RESPONSE | jq . +fi +echo "Ok: Graphite datasource exists." + +echo "Check if Prometheus/VictoriaMetrics Datasource exists in Grafana" +DS_STATUSCODE=$(curl --noproxy "$OITC_GRAFANA_HOSTNAME" "$OITC_GRAFANA_URL/api/datasources/name/Prometheus" -XGET -uadmin:$OITC_GRAFANA_ADMIN_PASSWORD -H 'Content-Type: application/json' -I 2>/dev/null | head -n 1 | cut -d$' ' -f2) +if [ "$DS_STATUSCODE" == "404" ]; then + echo "Create Prometheus/VictoriaMetrics Datasource for Grafana" + RESPONSE=$(curl --noproxy "$OITC_GRAFANA_HOSTNAME" "$OITC_GRAFANA_URL/api/datasources" -XPOST -uadmin:$OITC_GRAFANA_ADMIN_PASSWORD -H 'Content-Type: application/json' -d '{ + "name":"Prometheus", + "type":"prometheus", + "url":"http://victoriametrics:8428", + "access":"proxy", + "basicAuth":false, + "isDefault": false, + "jsonData": {} + }') + echo $RESPONSE | jq . +fi +echo "Ok: Prometheus/VictoriaMetrics datasource exists." + +if [ -f /opt/openitc/etc/grafana/api_key ]; then + echo "Check for Grafana Configuration in openITCOCKPIT database" + API_KEY=$(cat /opt/openitc/etc/grafana/api_key) + set +e + COUNT=$(mysql "--defaults-extra-file=$INIFILE" -e "SELECT COUNT(*) FROM grafana_configurations;" -B -s 2>/dev/null) + if [ "$?" == 0 ] && [ "$COUNT" == 0 ]; then + echo "Create missing default configuration." + # This is different from the default SETUP.sh! + mysql --defaults-extra-file=${INIFILE} -e "INSERT INTO grafana_configurations (api_url, api_key, graphite_prefix, use_https, use_proxy, ignore_ssl_certificate, dashboard_style, created, modified) VALUES('${OITC_GRAFANA_HOSTNAME}', '${API_KEY}', 'openitcockpit', 1, 0, 1, 'light', '2018-12-05 08:42:55', '2018-12-05 08:42:55');" + fi + set -e +fi + +echo "---------------------------------------------------------------" +echo "Scan and import ACL objects. This will take a while..." +oitc Acl.acl_extras aco_sync + + +oitc compress + +oitc setup --fast + +oitc nagios_export + +if [[ -d "/opt/openitc/nagios/rollout" ]]; then + if [[ ! -f "/opt/openitc/nagios/rollout/resource.cfg" ]]; then + ln -s /opt/openitc/nagios/etc/resource.cfg /opt/openitc/nagios/rollout/resource.cfg + fi +fi + +echo "Enabling webserver configuration" +if [[ ! -f "/etc/nginx/sites-enabled/openitc" ]]; then + ln -s /etc/nginx/sites-available/openitc /etc/nginx/sites-enabled/openitc +fi +rm -f /etc/nginx/sites-enabled/default + +#Set default permissions, check for always allowed permissions and dependencies +oitc roles --enable-defaults --admin + +date > /opt/openitc/var/.installation_done + + diff --git a/CONTAINER_UPDATE.sh b/CONTAINER_UPDATE.sh new file mode 100644 index 0000000000..c3c5be68fc --- /dev/null +++ b/CONTAINER_UPDATE.sh @@ -0,0 +1,405 @@ +#!/bin/bash +if [[ $1 == "--help" ]]; then + echo "Supported parameters:" + echo "--rights Reset file permissions" + echo "--cc Clear model cache" + + exit 0 +fi + +# Enable debug mode so that CakePHP will create missing folders +# https://github.com/it-novum/openITCOCKPIT/issues/1446 +# https://github.com/cakephp/migrations/issues/565 +export OITC_DEBUG=1 + +APPDIR="/opt/openitc/frontend" +INIFILE=/opt/openitc/etc/mysql/mysql.cnf +DUMPINIFILE=/opt/openitc/etc/mysql/dump.cnf +BASHCONF=/opt/openitc/etc/mysql/bash.conf + +OSVERSION=$(grep VERSION_CODENAME /etc/os-release | cut -d= -f2) +OS_BASE="debian" + +if [[ ! -f "$BASHCONF" ]]; then + MYSQL_USER=openitcockpit + MYSQL_DATABASE=openitcockpit + MYSQL_PASSWORD= + MYSQL_HOST=localhost + MYSQL_PORT=3306 + eval $(php -r "require '$APPDIR/src/itnovum/openITCOCKPIT/Database/MysqlConfigFileParserForCli.php'; \$mcp = new MysqlConfigFileParserForCli(); \$r = \$mcp->parse_mysql_cnf('/opt/openitc/etc/mysql/mysql.cnf'); echo \$r['shell'];") + + echo "dbc_dbuser='${MYSQL_USER}'" >$BASHCONF + echo "dbc_dbpass='${MYSQL_PASSWORD}'" >>$BASHCONF + echo "dbc_dbserver='${MYSQL_HOST}'" >>$BASHCONF + echo "dbc_dbport='${MYSQL_PORT}'" >>$BASHCONF + echo "dbc_dbname='${MYSQL_DATABASE}'" >>$BASHCONF +fi + +. /opt/openitc/etc/mysql/bash.conf + +echo "Copy required system files" +rsync -K -a ${APPDIR}/system/etc/. /etc/ # we use rsync because the destination can be a symlink on RHEL +chown root:root /etc +cp -r ${APPDIR}/system/usr/. /usr/ +cp ${APPDIR}/system/nginx/ssl_options_$OSVERSION /etc/nginx/openitc/ssl_options.conf +cp ${APPDIR}/system/nginx/ssl_cert.conf /etc/nginx/openitc/ssl_cert.conf +chmod +x /usr/bin/oitc + +echo "Create mysqldump of your current database" +BACKUP_TIMESTAMP=$(date '+%Y-%m-%d_%H-%M-%S') +BACKUP_DIR='/opt/openitc/nagios/backup' +mkdir -p $BACKUP_DIR +#If you have mysql binlog enabled uses this command: +#mysqldump --defaults-extra-file=${DUMPINIFILE} --databases $dbc_dbname --flush-privileges --single-transaction --master-data=1 --flush-logs --triggers --routines --events --hex-blob \ + +# ITC-2921 +# MySQL Bug: https://bugs.mysql.com/bug.php?id=109685 +# As with mysqldump 8.0.32 --single-transaction requires RELOAD or FLUSH_TABLES privilege(s) which the openitcockpit user does not have +# So for now the workaround is to remove "--single-transaction" +mysqldump --defaults-extra-file=${DUMPINIFILE} --databases $dbc_dbname --flush-privileges --triggers --routines --no-tablespaces --events --hex-blob \ + --ignore-table=$dbc_dbname.nagios_acknowledgements \ + --ignore-table=$dbc_dbname.nagios_commands \ + --ignore-table=$dbc_dbname.nagios_commenthistory \ + --ignore-table=$dbc_dbname.nagios_comments \ + --ignore-table=$dbc_dbname.nagios_configfiles \ + --ignore-table=$dbc_dbname.nagios_configfilevariables \ + --ignore-table=$dbc_dbname.nagios_conninfo \ + --ignore-table=$dbc_dbname.nagios_contact_addresses \ + --ignore-table=$dbc_dbname.nagios_contact_notificationcommands \ + --ignore-table=$dbc_dbname.nagios_contactgroup_members \ + --ignore-table=$dbc_dbname.nagios_contactgroups \ + --ignore-table=$dbc_dbname.nagios_contactnotificationmethods \ + --ignore-table=$dbc_dbname.nagios_contactnotifications \ + --ignore-table=$dbc_dbname.nagios_contacts \ + --ignore-table=$dbc_dbname.nagios_contactstatus \ + --ignore-table=$dbc_dbname.nagios_customvariables \ + --ignore-table=$dbc_dbname.nagios_customvariablestatus \ + --ignore-table=$dbc_dbname.nagios_dbversion \ + --ignore-table=$dbc_dbname.nagios_downtimehistory \ + --ignore-table=$dbc_dbname.nagios_eventhandlers \ + --ignore-table=$dbc_dbname.nagios_externalcommands \ + --ignore-table=$dbc_dbname.nagios_flappinghistory \ + --ignore-table=$dbc_dbname.nagios_host_contactgroups \ + --ignore-table=$dbc_dbname.nagios_host_contacts \ + --ignore-table=$dbc_dbname.nagios_host_parenthosts \ + --ignore-table=$dbc_dbname.nagios_hostchecks \ + --ignore-table=$dbc_dbname.nagios_hostdependencies \ + --ignore-table=$dbc_dbname.nagios_hostescalation_contactgroups \ + --ignore-table=$dbc_dbname.nagios_hostescalation_contacts \ + --ignore-table=$dbc_dbname.nagios_hostescalations \ + --ignore-table=$dbc_dbname.nagios_hostgroup_members \ + --ignore-table=$dbc_dbname.nagios_hostgroups \ + --ignore-table=$dbc_dbname.nagios_hosts \ + --ignore-table=$dbc_dbname.nagios_hoststatus \ + --ignore-table=$dbc_dbname.nagios_instances \ + --ignore-table=$dbc_dbname.nagios_logentries \ + --ignore-table=$dbc_dbname.nagios_notifications \ + --ignore-table=$dbc_dbname.nagios_processevents \ + --ignore-table=$dbc_dbname.nagios_programstatus \ + --ignore-table=$dbc_dbname.nagios_runtimevariables \ + --ignore-table=$dbc_dbname.nagios_scheduleddowntime \ + --ignore-table=$dbc_dbname.nagios_service_contactgroups \ + --ignore-table=$dbc_dbname.nagios_service_contacts \ + --ignore-table=$dbc_dbname.nagios_service_parentservices \ + --ignore-table=$dbc_dbname.nagios_servicechecks \ + --ignore-table=$dbc_dbname.nagios_servicedependencies \ + --ignore-table=$dbc_dbname.nagios_serviceescalation_contactgroups \ + --ignore-table=$dbc_dbname.nagios_serviceescalation_contacts \ + --ignore-table=$dbc_dbname.nagios_serviceescalations \ + --ignore-table=$dbc_dbname.nagios_servicegroup_members \ + --ignore-table=$dbc_dbname.nagios_servicegroups \ + --ignore-table=$dbc_dbname.nagios_services \ + --ignore-table=$dbc_dbname.nagios_servicestatus \ + --ignore-table=$dbc_dbname.nagios_statehistory \ + --ignore-table=$dbc_dbname.nagios_systemcommands \ + --ignore-table=$dbc_dbname.nagios_timedeventqueue \ + --ignore-table=$dbc_dbname.nagios_timedevents \ + --ignore-table=$dbc_dbname.nagios_timeperiod_timeranges \ + --ignore-table=$dbc_dbname.nagios_timeperiods \ + --ignore-table=$dbc_dbname.statusengine_dbversion \ + --ignore-table=$dbc_dbname.statusengine_host_acknowledgements \ + --ignore-table=$dbc_dbname.statusengine_host_downtimehistory \ + --ignore-table=$dbc_dbname.statusengine_host_notifications \ + --ignore-table=$dbc_dbname.statusengine_host_scheduleddowntimes \ + --ignore-table=$dbc_dbname.statusengine_host_statehistory \ + --ignore-table=$dbc_dbname.statusengine_hostchecks \ + --ignore-table=$dbc_dbname.statusengine_hoststatus \ + --ignore-table=$dbc_dbname.statusengine_logentries \ + --ignore-table=$dbc_dbname.statusengine_nodes \ + --ignore-table=$dbc_dbname.statusengine_perfdata \ + --ignore-table=$dbc_dbname.statusengine_service_acknowledgements \ + --ignore-table=$dbc_dbname.statusengine_service_downtimehistory \ + --ignore-table=$dbc_dbname.statusengine_service_notifications \ + --ignore-table=$dbc_dbname.statusengine_service_scheduleddowntimes \ + --ignore-table=$dbc_dbname.statusengine_service_statehistory \ + --ignore-table=$dbc_dbname.statusengine_servicechecks \ + --ignore-table=$dbc_dbname.statusengine_servicestatus \ + --ignore-table=$dbc_dbname.statusengine_tasks \ + --ignore-table=$dbc_dbname.statusengine_users \ + --ignore-table=$dbc_dbname.customalerts \ + --ignore-table=$dbc_dbname.customalert_statehistory \ + --ignore-table=$dbc_dbname.sla_availability_status_hosts_log \ + --ignore-table=$dbc_dbname.sla_availability_status_services_log \ + --ignore-table=$dbc_dbname.sla_host_outages \ + --ignore-table=$dbc_dbname.sla_service_outages \ + >$BACKUP_DIR/openitcockpit_dump_$BACKUP_TIMESTAMP.sql + +echo "---------------------------------------------------------------" +echo "Convert MySQL Tables from utf8_general_ci to utf8mb4_general_ci..." + +# Disabled - this takes ages! +#mysql --defaults-extra-file=${INIFILE} -e "ALTER DATABASE ${dbc_dbname} CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;" + +mysql --defaults-extra-file=${INIFILE} --batch --skip-column-names -e "SELECT TABLE_NAME FROM \`information_schema\`.\`TABLES\` WHERE \`TABLE_SCHEMA\`='${dbc_dbname}' AND \`TABLE_NAME\` NOT LIKE 'nagios_%' AND \`TABLE_COLLATION\`='utf8_general_ci'" | while read TABLE_NAME; do + echo "ALTER TABLE \`${TABLE_NAME}\` CONVERT TO CHARACTER SET utf8mb4; ✔" + mysql --defaults-extra-file=${INIFILE} -e "ALTER TABLE \`${TABLE_NAME}\` CONVERT TO CHARACTER SET utf8mb4;" +done + +echo "Running openITCOCKPIT Core database migration" +oitc migrations migrate + +oitc migrations seed + +echo "Running openITCOCKPIT Module database migration/s" +for PLUGIN in $(ls -1 "${APPDIR}/plugins"); do + if [[ "$PLUGIN" == *Module ]]; then + if [[ -d "${APPDIR}/plugins/${PLUGIN}/config/Migrations" ]]; then + echo "Running openITCOCKPIT ${PLUGIN} database migration" + oitc migrations migrate -p "${PLUGIN}" + fi + + if [[ -d "${APPDIR}/plugins/${PLUGIN}/config/Seeds" ]]; then + num_files=$(find "${APPDIR}/plugins/${PLUGIN}/config/Seeds" -mindepth 1 -iname "*.php" -type f | wc -l) + if [[ "$num_files" -gt 0 ]]; then + echo "Importing default records for ${PLUGIN} into database" + oitc migrations seed -p "${PLUGIN}" + fi + fi + + fi +done + +echo "---------------------------------------------------------------" +echo "Import extended openitcockpit-update module scripts ..." + +MODULE_SCRIPS_LOCK_DIR="/opt/openitc/var/updatesh_module_locks/" +MODULES_DIR="${APPDIR}/plugins/" +DEDICATED_MODULE_SCRIPTS_DIR_NAME="updateScripts" +LOADED_MODULE_SCRIPTS=() + +# include module scripts class header +echo "Import ${APPDIR}/bin/updatesh_module_scripts/system.h.sh" +. ${APPDIR}/bin/updatesh_module_scripts/system.h.sh ${APPDIR}/bin/updatesh_module_scripts/ +moduleList=($(ls -A1 $MODULES_DIR*/$DEDICATED_MODULE_SCRIPTS_DIR_NAME/*)) +for entry in "${moduleList[@]}" +do + if [[ $entry =~ \.h.sh$ ]]; then + echo "Import $entry" + shortName=`basename $entry` + realName=${shortName/".h.sh"/""} + path=${entry/$shortName/""} + LOADED_MODULE_SCRIPTS+=($realName) + . $entry $path + fi +done + +if type "system.property" &> /dev/null; then + system.property lockDir = $MODULE_SCRIPS_LOCK_DIR + system.property modulesDir = $MODULES_DIR + system.initialize +fi + +for Module in "${LOADED_MODULE_SCRIPTS[@]}"; do + if [ "$Module" != "system" ]; then + if type "${Module}" &> /dev/null; then + # create class object + ${Module} module + + # set required variables + module.property name = "${Module}" + module.property dbIniFile = "${Module}" + + module.initialize + fi + fi +done + +echo "---------------------------------------------------------------" +echo "Convert MySQL Tables from utf8_general_ci to utf8mb4_general_ci..." + +# Disabled - this takes ages! +#mysql --defaults-extra-file=${INIFILE} -e "ALTER DATABASE ${dbc_dbname} CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;" + +mysql --defaults-extra-file=${INIFILE} --batch --skip-column-names -e "SELECT TABLE_NAME FROM \`information_schema\`.\`TABLES\` WHERE \`TABLE_SCHEMA\`='${dbc_dbname}' AND \`TABLE_NAME\` NOT LIKE 'nagios_%' AND \`TABLE_COLLATION\`='utf8_general_ci'" | while read TABLE_NAME; do + echo "ALTER TABLE \`${TABLE_NAME}\` CONVERT TO CHARACTER SET utf8mb4; ✔" + mysql --defaults-extra-file=${INIFILE} -e "ALTER TABLE \`${TABLE_NAME}\` CONVERT TO CHARACTER SET utf8mb4;" +done + +#Compress and minify javascript files +oitc compress + +#Acc ALC dependencies config for itc core +echo "---------------------------------------------------------------" +echo "Scan for new user permissions. This will take a while..." +oitc Acl.acl_extras aco_sync + +#Set default permissions, check for always allowed permissions and dependencies +oitc roles --enable-defaults --admin + +echo "Checking that a server certificate for the openITCOCKPIT Monitoring Agent exists" +oitc agent --generate-server-ca + +# ITC-1911 +echo "Cleanup for invalid parent hosts on satellite instance" +oitc ParentHostsVisibilityCleaning + +for i in "$@"; do + case $i in + --cc) + echo "Clear out Model Cache /opt/openitc/frontend/tmp/cache/models/" + rm -rf /opt/openitc/frontend/tmp/cache/models/* + echo "Clear out CLI Model Cache /opt/openitc/frontend/tmp/cli/cache/cli/models/" + rm -rf /opt/openitc/frontend/tmp/cli/cache/cli/models/* + ;; + + --rights) + oitc rights + ;; + + *) + #No default at the moment + ;; + esac +done + +echo "Flush redis cache" +redis-cli -h $OITC_REDIS_HOST -p $OITC_REDIS_PORT FLUSHALL +echo "" + +echo "Copy required system files" +rsync -K -a ${APPDIR}/system/etc/. /etc/ +chown root:root /etc +cp -r ${APPDIR}/system/usr/. /usr/ +cp ${APPDIR}/system/nginx/ssl_options_$OSVERSION /etc/nginx/openitc/ssl_options.conf +# only ensure that the files exist +touch /etc/nginx/openitc/ssl_cert.conf +touch /etc/nginx/openitc/custom.conf + + +chmod +x /usr/bin/oitc + +echo "Create required system folders" +mkdir -p /opt/openitc/etc/{mysql,grafana,carbon,frontend,nagios,nsta} +mkdir -p /opt/openitc/nagios/etc/config +mkdir -p /opt/openitc/etc/nagios/nagios.cfg.d + +chown www-data:www-data /opt/openitc/logs/frontend +chown nagios:www-data /opt/openitc/nagios/etc/config +chown nagios:www-data /opt/openitc/etc/nagios/nagios.cfg.d +chmod 775 /opt/openitc/logs/frontend + +chown www-data:www-data /opt/openitc/frontend/tmp + +mkdir -p /opt/openitc/frontend/webroot/img/charts +chown www-data:www-data /opt/openitc/frontend/webroot/img/charts + +if [[ -d /opt/openitc/frontend/plugins/MapModule/webroot/img/ ]]; then + chown -R www-data:www-data /opt/openitc/frontend/plugins/MapModule/webroot/img/ +fi + +if getent group ssl-cert &>/dev/null; then + if [ -z "$(groups nagios | grep ssl-cert)" ]; then + usermod -aG ssl-cert nagios + fi +fi + +oitc config_generator_shell --generate-container +oitc nagios_export --resource + +if [[ -d "/opt/openitc/nagios/rollout" ]]; then + if [[ ! -f "/opt/openitc/nagios/rollout/resource.cfg" ]]; then + ln -s /opt/openitc/nagios/etc/resource.cfg /opt/openitc/nagios/rollout/resource.cfg + fi +fi + +echo $OITC_GRAFANA_ADMIN_PASSWORD > /opt/openitc/etc/grafana/admin_password + +if [ -f /opt/openitc/etc/grafana/api_key ]; then + echo "Check if Grafana is reachable" + COUNTER=0 + + set +e + while [ "$COUNTER" -lt 30 ]; do + echo "Try to connect to Grafana API..." + #Is Grafana Server Online? + STATUSCODE=$(curl --noproxy "$OITC_GRAFANA_HOSTNAME" "$OITC_GRAFANA_URL/api/admin/stats" -XGET -uadmin:$OITC_GRAFANA_ADMIN_PASSWORD -H 'Content-Type: application/json' -I 2>/dev/null | head -n 1 | cut -d$' ' -f2) + + if [ "$STATUSCODE" == "200" ]; then + echo "Check if Prometheus/VictoriaMetrics Datasource exists in Grafana" + DS_STATUSCODE=$(curl --noproxy "$OITC_GRAFANA_HOSTNAME" "$OITC_GRAFANA_URL/api/datasources/name/Prometheus" -XGET -uadmin:$OITC_GRAFANA_ADMIN_PASSWORD -H 'Content-Type: application/json' -I 2>/dev/null | head -n 1 | cut -d$' ' -f2) + + if [ "$DS_STATUSCODE" == "404" ]; then + echo "Create Prometheus/VictoriaMetrics Datasource for Grafana" + RESPONSE=$(curl --noproxy "$OITC_GRAFANA_HOSTNAME" "$OITC_GRAFANA_URL/api/datasources" -XPOST -uadmin:$OITC_GRAFANA_ADMIN_PASSWORD -H 'Content-Type: application/json' -d '{ + "name":"Prometheus", + "type":"prometheus", + "url":"http://victoriametrics:8428", + "access":"proxy", + "basicAuth":false, + "isDefault": false, + "jsonData": {} + }') + echo $RESPONSE | jq . + fi + echo "Ok: Prometheus/VictoriaMetrics datasource exists." + COUNTER=9999 # break while loop bc bash where break does not brake + break + fi + COUNTER=$((COUNTER + 1)) + sleep 1 + done + + if [ ! -f /opt/openitc/etc/grafana/api_key ]; then + echo "ERROR!" + echo "Could not connect to Grafana" + fi + set -e +fi + +echo "Restart monitoring engine" +oitc supervisor restart naemon + +echo "Enabling webserver configuration" +if [[ ! -f "/etc/nginx/sites-enabled/openitc" ]]; then + ln -s /etc/nginx/sites-available/openitc /etc/nginx/sites-enabled/openitc +fi +rm -f /etc/nginx/sites-enabled/default + +set +e + +# todo fix this +#for Module in "${LOADED_MODULE_SCRIPTS[@]}"; do +# if [ "$Module" != "system" ]; then +# if type "${Module}" &> /dev/null; then +# # create class object +# ${Module} module +# +# # set required variables +# module.property name = "${Module}" +# module.property dbIniFile = "${Module}" +# +# module.finish +# fi +# fi +#done + +# Set filesystem permissions after all is done - again +#todo fix if realy required +#chown www-data:www-data /opt/openitc/logs/frontend +#oitc rights +#chown nagios:nagios /opt/openitc/logs/frontend/nagios +#chown www-data:www-data /opt/openitc/frontend/ +#chmod 775 /opt/openitc/logs/frontend +#chmod 775 /opt/openitc/logs/frontend/nagios +#chown www-data:www-data /opt/openitc/frontend/tmp + diff --git a/composer.json b/composer.json index ab8c808515..bb4416ea17 100644 --- a/composer.json +++ b/composer.json @@ -24,6 +24,7 @@ "require": { "php": ">=7.2", "ext-calendar": "*", + "ext-curl": "*", "ext-gd": "*", "ext-gearman": "*", "ext-json": "*", @@ -34,6 +35,7 @@ "ext-simplexml": "*", "ext-soap": "*", "ext-sockets": "*", + "ext-xmlrpc": "*", "ext-zip": "*", "azuyalabs/yasumi": "^2.2", "cakephp/authentication": "^2.0", @@ -47,7 +49,6 @@ "erusev/parsedown": "^1.7", "erusev/parsedown-extra": "^0.8", "freedsx/ldap": "^0.5.0", - "friendsofcake/cakepdf": "4.0.0-beta", "friendsofcake/cakephp-csvview": "^4.0", "guiguiboy/php-cli-progress-bar": "^0.0.4", "guzzlehttp/guzzle": "^7.4", diff --git a/config/app.php b/config/app.php index 9b81b90cec..cc95e823dc 100644 --- a/config/app.php +++ b/config/app.php @@ -17,6 +17,11 @@ */ 'debug' => filter_var(env('OITC_DEBUG', false), FILTER_VALIDATE_BOOLEAN), + /** + * If set to true, openITCOCKPIT is running inside a container like Docker + */ + 'container' => filter_var(env('IS_CONTAINER', false), FILTER_VALIDATE_BOOLEAN), + /** * Configure basic information about the application. * @@ -74,7 +79,7 @@ * You should treat it as extremely sensitive data. */ 'Security' => [ - 'salt' => 'cf4515a2c1833f4aed69591f81598da0124cbd460449b2812495a64d8d70aadc' + 'salt' => env('OITC_SECURITY_SALT', 'cf4515a2c1833f4aed69591f81598da0124cbd460449b2812495a64d8d70aadc') ], /** @@ -105,8 +110,8 @@ 'serialize' => true, 'prefix' => 'oitc_', 'duration' => '+30 minute', - 'host' => '127.0.0.1', - 'port' => 6379 + 'host' => env('OITC_REDIS_HOST', '127.0.0.1'), + 'port' => filter_var(env('OITC_REDIS_PORT', 6379), FILTER_VALIDATE_INT) ], 'permissions' => [ @@ -114,8 +119,8 @@ 'serialize' => true, 'prefix' => 'permissions_', 'duration' => '+600 seconds', - 'host' => '127.0.0.1', - 'port' => 6379 + 'host' => env('OITC_REDIS_HOST', '127.0.0.1'), + 'port' => filter_var(env('OITC_REDIS_PORT', 6379), FILTER_VALIDATE_INT) ], 'long_time_cache' => [ @@ -123,8 +128,8 @@ 'serialize' => true, 'prefix' => 'ltc_', 'duration' => '+24 hours', - 'host' => '127.0.0.1', - 'port' => 6379 + 'host' => env('OITC_REDIS_HOST', '127.0.0.1'), + 'port' => filter_var(env('OITC_REDIS_PORT', 6379), FILTER_VALIDATE_INT) ], /** @@ -203,7 +208,7 @@ */ 'Error' => [ 'errorLevel' => E_ALL, - 'exceptionRenderer' => \Cake\Error\ExceptionRenderer::class, + 'exceptionRenderer' => \Cake\Error\Renderer\WebExceptionRenderer::class, 'skipLog' => [], 'log' => true, 'trace' => true, @@ -280,6 +285,6 @@ * To use database sessions, load the SQL file located at config/schema/sessions.sql */ 'Session' => [ - 'defaults' => env('SESSION_DEFAULTS', 'php'), + 'defaults' => env('OITC_SESSION_DEFAULTS', 'php'), ], ]; diff --git a/config/bootstrap.php b/config/bootstrap.php index 73d943b9f2..b73f6fa9ae 100644 --- a/config/bootstrap.php +++ b/config/bootstrap.php @@ -112,6 +112,13 @@ } } +if (!defined('IS_CONTAINER')) { + if (Configure::read('container') === true) { + define('IS_CONTAINER', true); + } else { + define('IS_CONTAINER', false); + } +} /* * Load an environment local configuration file to provide overrides to your configuration. @@ -243,29 +250,3 @@ //Inflector::rules('uninflected', ['dontinflectme']); //Inflector::rules('transliteration', ['/å/' => 'aa']); - -//Configure CakePHP and WkHtmlToPdf -//default path of apt-get install wkhtmltopdf (Mostly old and broken) -$WkHtmlToPdfBinary = '/usr/bin/wkhtmltopdf'; -if (file_exists('/usr/local/bin/wkhtmltopdf')) { - //Installed from deb file from https://wkhtmltopdf.org/ (newer version and working) - $WkHtmlToPdfBinary = '/usr/local/bin/wkhtmltopdf'; -} -Configure::write('CakePdf', [ - 'engine' => [ - 'className' => 'CakePdf.WkHtmlToPdf', - // Mac OS X / Linux is usually like: - 'binary' => $WkHtmlToPdfBinary, - // On Windows environmnent you NEED to use the path like - // old fashioned MS-DOS Paths, otherwise you will keep getting: - // WKHTMLTOPDF didn't return any data - // 'binary' => 'C:\\Progra~1\\wkhtmltopdf\\bin\\wkhtmltopdf.exe', - // 'cwd' => 'C:\\Progra~1\\wkhtmltopdf\\bin', - 'options' => [ - 'print-media-type' => true, - 'outline' => true, - 'dpi' => 96, - 'enable-local-file-access' => true, - ], - ], -]); diff --git a/config/paths.php b/config/paths.php index 25b428485c..650412835d 100644 --- a/config/paths.php +++ b/config/paths.php @@ -61,6 +61,10 @@ $isCli = PHP_SAPI === 'cli'; +// This var determins if openITCOCKPIT is running inside a workhorse container like mod_gearman +// It yes, we use different path for logfiles and caching to avoid permission issues +$isWorkhorseContainer = filter_var(env('IS_WORKHORSE_CONTAINER', false), FILTER_VALIDATE_BOOLEAN); + /* * Path to the tests directory. */ @@ -69,47 +73,117 @@ /* * Path to the temporary files directory. */ -if ($isCli === false) { - // www-data - define('TMP', ROOT . DS . 'tmp' . DS); + +if (!isset($_SERVER['USER'])) { + // Must be inside a container or some other scatchy setup + $_SERVER['USER'] = 'root'; + if (getmyuid() > 0) { + // Probably we are nagios? + $_SERVER['USER'] = 'nagios'; + } +} + +if ($isWorkhorseContainer === false) { + // Default installation of openITCOCKPIT via apt, dnf or git + // Also used if openITCOCKPIT is running inside a container like docker + if ($isCli === false) { + // www-data + define('TMP', ROOT . DS . 'tmp' . DS); + } else { + // root or nagios + if ($_SERVER['USER'] !== 'root') { + //nagios user or so + define('TMP', ROOT . DS . 'tmp' . DS . 'nagios' . DS); + } else { + //root user + define('TMP', ROOT . DS . 'tmp' . DS . 'cli' . DS); + } + } } else { - // root or nagios - if ($_SERVER['USER'] !== 'root') { - //nagios user or so - define('TMP', ROOT . DS . 'tmp' . DS . 'nagios' . DS); + // This openITCOCKPIT is running inside a workhorse container like mod_gearman + // The primary job of this container is only to provide the openITCOCKPIT CLI backend to be able + // to execute notification commands or the evc check plugin + if ($isCli === false) { + // www-data + define('TMP', DS . 'tmp' . DS . 'openitcockpit' . DS . 'tmp'); // /tmp/openitcockpit/tmp/ } else { - //root user - define('TMP', ROOT . DS . 'tmp' . DS . 'cli' . DS); + // root or nagios + if ($_SERVER['USER'] !== 'root') { + //nagios user or so + define('TMP', DS . 'tmp' . DS . 'openitcockpit' . DS . 'tmp' . DS . 'nagios' . DS); // /tmp/openitcockpit/tmp/nagios + } else { + //root user + define('TMP', DS . 'tmp' . DS . 'openitcockpit' . DS . 'tmp' . DS . 'cli' . DS); // /tmp/openitcockpit/tmp/cli + } } } + /* * Path to the logs directory. */ -if ($isCli === false) { - //www-data - define('LOGS', ROOT . DS . 'logs' . DS); +if ($isWorkhorseContainer === false) { + // Default installation of openITCOCKPIT via apt, dnf or git + // Also used if openITCOCKPIT is running inside a container like docker + if ($isCli === false) { + //www-data + define('LOGS', ROOT . DS . 'logs' . DS); + } else { + if ($_SERVER['USER'] !== 'root') { + define('LOGS', ROOT . DS . 'logs' . DS . 'nagios' . DS); + } else { + define('LOGS', ROOT . DS . 'logs' . DS); + } + } } else { - if ($_SERVER['USER'] !== 'root') { - define('LOGS', ROOT . DS . 'logs' . DS . 'nagios' . DS); + // This openITCOCKPIT is running inside a workhorse container like mod_gearman + // The primary job of this container is only to provide the openITCOCKPIT CLI backend to be able + // to execute notification commands or the evc check plugin + if ($isCli === false) { + //www-data + define('LOGS', DS . 'tmp' . DS . 'openitcockpit' . DS . 'logs' . DS); // /tmp/openitcockpit/logs/ } else { - define('LOGS', ROOT . DS . 'logs' . DS); + if ($_SERVER['USER'] !== 'root') { + define('LOGS', DS . 'tmp' . DS . 'openitcockpit' . DS . 'logs' . DS . 'nagios' . DS); // /tmp/openitcockpit/logs/nagios + } else { + define('LOGS', DS . 'tmp' . DS . 'openitcockpit' . DS . 'logs' . DS); // /tmp/openitcockpit/logs/ + } } } /* * Path to the cache files directory. It can be shared between hosts in a multi-server setup. */ -if ($isCli === false) { - //www-data - define('CACHE', TMP . 'cache' . DS); +if ($isWorkhorseContainer === false) { + // Default installation of openITCOCKPIT via apt, dnf or git + // Also used if openITCOCKPIT is running inside a container like docker + if ($isCli === false) { + //www-data + define('CACHE', TMP . 'cache' . DS); + } else { + if ($_SERVER['USER'] !== 'root') { + //nagios user or so + define('CACHE', TMP . 'cache' . DS . 'nagios' . DS); + } else { + //root user + define('CACHE', TMP . 'cache' . DS . 'cli' . DS); + } + } } else { - if ($_SERVER['USER'] !== 'root') { - //nagios user or so - define('CACHE', TMP . 'cache' . DS . 'nagios' . DS); + // This openITCOCKPIT is running inside a workhorse container like mod_gearman + // The primary job of this container is only to provide the openITCOCKPIT CLI backend to be able + // to execute notification commands or the evc check plugin + if ($isCli === false) { + //www-data + define('CACHE', DS . 'tmp' . DS . 'openitcockpit' . DS . 'cache' . DS); // /tmp/openitcockpit/cache/ } else { - //root user - define('CACHE', TMP . 'cache' . DS . 'cli' . DS); + if ($_SERVER['USER'] !== 'root') { + //nagios user or so + define('CACHE', DS . 'tmp' . DS . 'openitcockpit' . DS . 'cache' . DS . 'nagios' . DS); // /tmp/openitcockpit/cache/nagios/ + } else { + //root user + define('CACHE', DS . 'tmp' . DS . 'openitcockpit' . DS . 'cache' . DS . 'cli' . DS); // /tmp/openitcockpit/cache/cli/ + } } } diff --git a/plugins/GrafanaModule/src/Command/GrafanaDashboardCommand.php b/plugins/GrafanaModule/src/Command/GrafanaDashboardCommand.php index 29d48d5530..1237fe1f4b 100644 --- a/plugins/GrafanaModule/src/Command/GrafanaDashboardCommand.php +++ b/plugins/GrafanaModule/src/Command/GrafanaDashboardCommand.php @@ -38,7 +38,6 @@ use itnovum\openITCOCKPIT\Grafana\GrafanaTargetWhisper; use itnovum\openITCOCKPIT\Grafana\GrafanaThresholdCollection; use itnovum\openITCOCKPIT\Grafana\GrafanaThresholds; -use itnovum\openITCOCKPIT\Grafana\GrafanaYAxes; use Statusengine\PerfdataParser; /** @@ -352,7 +351,6 @@ public function getJsonForImport($hostId, HostsTable $HostsTable, ServicestatusT $grafanaTargetCollection, new GrafanaOverrides($grafanaTargetCollection), new GrafanaColorOverrides($grafanaTargetCollection), - new GrafanaYAxes($grafanaTargetCollection), new GrafanaThresholdCollection($grafanaTargetCollection) ); diff --git a/plugins/GrafanaModule/src/Controller/GrafanaUserdashboardsController.php b/plugins/GrafanaModule/src/Controller/GrafanaUserdashboardsController.php index 4dda62f5b3..1825bdd8c7 100644 --- a/plugins/GrafanaModule/src/Controller/GrafanaUserdashboardsController.php +++ b/plugins/GrafanaModule/src/Controller/GrafanaUserdashboardsController.php @@ -61,7 +61,6 @@ use itnovum\openITCOCKPIT\Grafana\GrafanaTargetWhisper; use itnovum\openITCOCKPIT\Grafana\GrafanaThresholdCollection; use itnovum\openITCOCKPIT\Grafana\GrafanaThresholds; -use itnovum\openITCOCKPIT\Grafana\GrafanaYAxes; use Statusengine\PerfdataParser; /** @@ -872,7 +871,6 @@ public function synchronizeWithGrafana($id = null) { $GrafanaTargetCollection, new GrafanaOverrides($GrafanaTargetCollection), new GrafanaColorOverrides($GrafanaTargetCollection), - new GrafanaYAxes($GrafanaTargetCollection), new GrafanaThresholdCollection($GrafanaTargetCollection) ); $GrafanaRow->addPanel($GrafanaPanel); diff --git a/plugins/NagiosModule/updateScripts/NagiosModule.class.sh b/plugins/NagiosModule/updateScripts/NagiosModule.class.sh index 7c5678a4ad..0a3756777a 100644 --- a/plugins/NagiosModule/updateScripts/NagiosModule.class.sh +++ b/plugins/NagiosModule/updateScripts/NagiosModule.class.sh @@ -32,9 +32,15 @@ NagiosModule.initialize() { NagiosModule.installIfNeeded() { echo "Run $(NagiosModule.property name) update" - chown nagios:www-data -R /opt/openitc/nagios/etc - chown nagios:www-data -R /opt/openitc/nagios/var - chown nagios:www-data -R /opt/openitc/nagios/share + if [ -d "/opt/openitc/nagios/etc" ]; then + chown nagios:www-data -R /opt/openitc/nagios/etc + fi + if [ -d "/opt/openitc/nagios/var" ]; then + chown nagios:www-data -R /opt/openitc/nagios/var + fi + if [ -d "/opt/openitc/nagios/share" ]; then + chown nagios:www-data -R /opt/openitc/nagios/share + fi system.lock $(NagiosModule.property name) "installed" $(NagiosModule.property disableFileLock) if [ $(NagiosModule.property disableFileLock) == 1 ]; then @@ -55,10 +61,11 @@ NagiosModule.installIfNeeded() { } NagiosModule.install() { - if [ -f "/opt/openitc/etc/nagios/nagios.cfg" ]; then - echo "Start service: nagios.service" - systemctl start nagios.service - fi + echo "NagiosModule.install" + #if [ -f "/opt/openitc/etc/nagios/nagios.cfg" ]; then + # echo "Start service: nagios.service" + # systemctl start nagios.service + #fi } NagiosModule.uninstall() { diff --git a/plugins/PuppeteerPdf/src/Pdf/PuppeteerClient.php b/plugins/PuppeteerPdf/src/Pdf/PuppeteerClient.php index 7cee1fe2b8..6740e1d8d0 100644 --- a/plugins/PuppeteerPdf/src/Pdf/PuppeteerClient.php +++ b/plugins/PuppeteerPdf/src/Pdf/PuppeteerClient.php @@ -26,6 +26,11 @@ class PuppeteerClient { private $address = 'http://127.0.0.1:7084/'; public function __construct() { + $address = env('OITC_PUPPETEER_ADDRESS', null); + if (!empty($address)) { + $this->address = $address; + } + $this->Client = new Client([ 'base_uri' => $this->address, 'proxy' => [ diff --git a/plugins/Statusengine3Module/updateScripts/Statusengine3Module.class.sh b/plugins/Statusengine3Module/updateScripts/Statusengine3Module.class.sh index 14d5e0aa49..9ee0c7d6f4 100644 --- a/plugins/Statusengine3Module/updateScripts/Statusengine3Module.class.sh +++ b/plugins/Statusengine3Module/updateScripts/Statusengine3Module.class.sh @@ -62,10 +62,11 @@ Statusengine3Module.uninstall(){ } Statusengine3Module.finish(){ - if [ $(system.moduleSrcDirExists $(Statusengine3Module.property name)) == 1 ]; then - if systemctl is-active --quiet statusengine.service; then - echo "Restart service: statusengine.service" - systemctl restart statusengine.service - fi - fi + echo "Statusengine3Module.finish" + #if [ $(system.moduleSrcDirExists $(Statusengine3Module.property name)) == 1 ]; then + # if systemctl is-active --quiet statusengine.service; then + # echo "Restart service: statusengine.service" + # systemctl restart statusengine.service + # fi + #fi } diff --git a/src/Command/ConfigGeneratorShellCommand.php b/src/Command/ConfigGeneratorShellCommand.php index 088c973f6b..b4408075a2 100644 --- a/src/Command/ConfigGeneratorShellCommand.php +++ b/src/Command/ConfigGeneratorShellCommand.php @@ -54,9 +54,10 @@ public function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionPar $parser = parent::buildOptionParser($parser); $parser->addOptions([ - 'generate' => ['help' => "Will generate all configuration files from database", 'boolean' => true, 'default' => false], - 'reload' => ['help' => "Reload services, where a new configuration file was generated for", 'boolean' => true, 'default' => false], - 'migrate' => ['help' => 'Will migrate existing configuration files to database', 'boolean' => true, 'default' => false] + 'generate' => ['help' => "Will generate all configuration files from database", 'boolean' => true, 'default' => false], + 'generate-container' => ['help' => "Will generate all configuration files from environment variables. Only use this option if openITCOCKPIT is running inside a container.", 'boolean' => true, 'default' => false], + 'reload' => ['help' => "Reload services, where a new configuration file was generated for", 'boolean' => true, 'default' => false], + 'migrate' => ['help' => 'Will migrate existing configuration files to database', 'boolean' => true, 'default' => false] ]); return $parser; @@ -82,6 +83,7 @@ public function execute(Arguments $args, ConsoleIo $io) { } if ($args->getOption('generate')) { + // Full installation of openITCOCKPIT (via apt, dnf or git clone) $hadJob = true; if ($args->getOption('reload')) { $this->generateAndReload($io); @@ -90,6 +92,12 @@ public function execute(Arguments $args, ConsoleIo $io) { } } + if ($args->getOption('generate-container')) { + // openITCOCKPIT is running inside a docker container + $hadJob = true; + $this->generateFromEnvironmentVariables($io); + } + if (!$hadJob) { $this->displayHelp($this->getOptionParser(), $args, $io); } @@ -113,6 +121,24 @@ private function generate(ConsoleIo $io) { } } + /** + * @param ConsoleIo $io + */ + private function generateFromEnvironmentVariables(ConsoleIo $io) { + $io->out('Generate all configuration files from environment variables... '); + /** @var ConfigurationFilesTable $ConfigurationFilesTable */ + $ConfigurationFilesTable = TableRegistry::getTableLocator()->get('ConfigurationFiles'); + + $GeneratorRegistry = new GeneratorRegistry(); + + foreach ($GeneratorRegistry->getAllConfigFilesForContainer() as $ConfigFileObject) { + /** @var ConfigInterface $ConfigFileObject */ + $io->out(sprintf('Generate %s ', $ConfigFileObject->getLinkedOutfile()), 0); + $ConfigFileObject->writeToFile($ConfigFileObject->getValuesFromEnvironment()); + $io->success('Ok'); + } + } + /** * @param ConsoleIo $io */ diff --git a/src/Command/GearmanWorkerCommand.php b/src/Command/GearmanWorkerCommand.php index e1f8e7ec9b..e1899474dd 100644 --- a/src/Command/GearmanWorkerCommand.php +++ b/src/Command/GearmanWorkerCommand.php @@ -30,6 +30,8 @@ use App\itnovum\openITCOCKPIT\Database\Backup; use App\itnovum\openITCOCKPIT\Monitoring\Naemon\ExternalCommands; +use App\itnovum\openITCOCKPIT\Supervisor\Binarydctl; +use App\itnovum\openITCOCKPIT\Supervisor\Supervisorctl; use App\Model\Entity\Changelog; use App\Model\Table\AgentconfigsTable; use App\Model\Table\ChangelogsTable; @@ -752,26 +754,49 @@ public function runJob($job) { break; case 'export_verify_config': + if (IS_CONTAINER === false) { + // Normal installation of openITCOCKPIT via apt, dnf or git + /** @var SystemsettingsTable $SystemsettingsTable */ + $SystemsettingsTable = TableRegistry::getTableLocator()->get('Systemsettings'); + $systemsettings = $SystemsettingsTable->findAsArray(); - /** @var SystemsettingsTable $SystemsettingsTable */ - $SystemsettingsTable = TableRegistry::getTableLocator()->get('Systemsettings'); - $systemsettings = $SystemsettingsTable->findAsArray(); + $naemonBin = Configure::read('nagios.basepath') . Configure::read('nagios.bin') . Configure::read('nagios.nagios_bin'); + $naemonCfg = Configure::read('nagios.nagios_cfg'); - $naemonBin = Configure::read('nagios.basepath') . Configure::read('nagios.bin') . Configure::read('nagios.nagios_bin'); - $naemonCfg = Configure::read('nagios.nagios_cfg'); + $cmd = sprintf( + 'sudo -u %s %s -v %s', + escapeshellarg($systemsettings['MONITORING']['MONITORING.USER']), + $naemonBin, + $naemonCfg + ); - $cmd = sprintf( - 'sudo -u %s %s -v %s', - escapeshellarg($systemsettings['MONITORING']['MONITORING.USER']), - $naemonBin, - $naemonCfg - ); + exec($cmd, $output, $returncode); + $return = [ + 'output' => $output, + 'returncode' => $returncode, + ]; + } else { + // openITCOCKPIT is running in a container like docker + $Binarydctl = new Binarydctl(); + // Naemon is running in a remote container, so we can only communicate through the XML RCP API of Supervisor + $BinarydEndpoint = $Binarydctl->getBinarydApiEndpointByServiceName('naemon-verify'); - exec($cmd, $output, $returncode); - $return = [ - 'output' => $output, - 'returncode' => $returncode, - ]; + //Run naemon-verify + try { + $result = $BinarydEndpoint->executeJson('naemon-verify'); + $returncode = $result['rc'] ?? 1; + $output = [ + $result['stdout'] ?? '' + ]; + } catch (\Exception $e) { + Log::error($e->getMessage()); + } + + $return = [ + 'output' => $output ?? ['Unknown'], + 'returncode' => $returncode ?? 1, + ]; + } break; case 'export_verify_prometheus_config': @@ -897,49 +922,68 @@ public function runJob($job) { 'isNodeJsServerRunning' => false ]; - exec($systemsetting['MONITORING']['MONITORING.STATUS'] . $errorRedirect, $output, $returncode); - if ($returncode == 0) { - $state['isNagiosRunning'] = true; - } + if (IS_CONTAINER === false) { + // Normal installation of openITCOCKPIT via apt, dnf or git + exec($systemsetting['MONITORING']['MONITORING.STATUS'] . $errorRedirect, $output, $returncode); + if ($returncode == 0) { + $state['isNagiosRunning'] = true; + } - exec($systemsetting['INIT']['INIT.NDO_STATUS'] . $errorRedirect, $output, $returncode); - if ($returncode == 0) { - $state['isNdoRunning'] = true; - } + exec($systemsetting['INIT']['INIT.NDO_STATUS'] . $errorRedirect, $output, $returncode); + if ($returncode == 0) { + $state['isNdoRunning'] = true; + } - exec($systemsetting['INIT']['INIT.STATUSENIGNE_STATUS'] . $errorRedirect, $output, $returncode); - if ($returncode == 0) { - $state['isStatusengineRunning'] = true; - } + exec($systemsetting['INIT']['INIT.STATUSENIGNE_STATUS'] . $errorRedirect, $output, $returncode); + if ($returncode == 0) { + $state['isStatusengineRunning'] = true; + } - exec($systemsetting['INIT']['INIT.NPCD_STATUS'] . $errorRedirect, $output, $returncode); - if ($returncode == 0) { - $state['isNpcdRunning'] = true; - } + exec($systemsetting['INIT']['INIT.NPCD_STATUS'] . $errorRedirect, $output, $returncode); + if ($returncode == 0) { + $state['isNpcdRunning'] = true; + } - exec($systemsetting['INIT']['INIT.OITC_CMD_STATUS'] . $errorRedirect, $output, $returncode); - if ($returncode == 0) { - $state['isOitcCmdRunning'] = true; - } + exec($systemsetting['INIT']['INIT.OITC_CMD_STATUS'] . $errorRedirect, $output, $returncode); + if ($returncode == 0) { + $state['isOitcCmdRunning'] = true; + } - exec($systemsetting['INIT']['INIT.SUDO_SERVER_STATUS'] . $errorRedirect, $output, $returncode); - if ($returncode == 0) { - $state['isSudoServerRunning'] = true; - } + exec($systemsetting['INIT']['INIT.SUDO_SERVER_STATUS'] . $errorRedirect, $output, $returncode); + if ($returncode == 0) { + $state['isSudoServerRunning'] = true; + } - exec($systemsetting['INIT']['INIT.NSTA_STATUS'] . $errorRedirect, $output, $returncode); - if ($returncode == 0) { - $state['isNstaRunning'] = true; - } + exec($systemsetting['INIT']['INIT.NSTA_STATUS'] . $errorRedirect, $output, $returncode); + if ($returncode == 0) { + $state['isNstaRunning'] = true; + } - exec($systemsetting['INIT']['INIT.PUSH_NOTIFICATION'] . $errorRedirect, $output, $returncode); - if ($returncode == 0) { - $state['isPushNotificationRunning'] = true; - } + exec($systemsetting['INIT']['INIT.PUSH_NOTIFICATION'] . $errorRedirect, $output, $returncode); + if ($returncode == 0) { + $state['isPushNotificationRunning'] = true; + } + + exec($systemsetting['INIT']['INIT.OPENITCOCKPIT_NODE'] . $errorRedirect, $output, $returncode); + if ($returncode == 0) { + $state['isNodeJsServerRunning'] = true; + } + } else { + // openITCOCKPIT is running inside a container like docker + $Supervisorctl = new Supervisorctl(); + $state = [ + 'isNagiosRunning' => $Supervisorctl->isRunning('naemon'), + 'isNdoRunning' => false, + 'isStatusengineRunning' => $Supervisorctl->isRunning('statusengine'), + 'isNpcdRunning' => false, + 'isOitcCmdRunning' => $Supervisorctl->isRunning('oitc_cmd'), + 'isSudoServerRunning' => $Supervisorctl->isRunning('sudo_server'), + 'isNstaRunning' => $Supervisorctl->isRunning('nsta'), + 'isGearmanWorkerRunning' => true, + 'isPushNotificationRunning' => $Supervisorctl->isRunning('push_notification'), + 'isNodeJsServerRunning' => $Supervisorctl->isRunning('openitcockpit-node') + ]; - exec($systemsetting['INIT']['INIT.OPENITCOCKPIT_NODE'] . $errorRedirect, $output, $returncode); - if ($returncode == 0) { - $state['isNodeJsServerRunning'] = true; } $return = $state; @@ -1332,16 +1376,38 @@ public function launchExport($createBackup = 1) { $SystemsettingsTable = TableRegistry::getTableLocator()->get('Systemsettings'); $systemsettings = $SystemsettingsTable->findAsArray(); - $naemonBin = Configure::read('nagios.basepath') . Configure::read('nagios.bin') . Configure::read('nagios.nagios_bin'); - $naemonCfg = Configure::read('nagios.nagios_cfg'); - $cmd = sprintf( - 'sudo -u %s %s -v %s', - escapeshellarg($systemsettings['MONITORING']['MONITORING.USER']), - $naemonBin, - $naemonCfg - ); - $output = null; - exec($cmd, $output, $returncode); + $Supervisorctl = new Supervisorctl(); + + if (IS_CONTAINER) { + // Naemon is running inside a container - query remote supervisor to run "naemon -v naemon.cfg" + // openITCOCKPIT is running in a container like docker + $Binarydctl = new Binarydctl(); + // Naemon is running in a remote container, so we can only communicate through the XML RCP API of Supervisor + $BinarydEndpoint = $Binarydctl->getBinarydApiEndpointByServiceName('naemon-verify'); + + //Run naemon-verify + try { + $result = $BinarydEndpoint->executeJson('naemon-verify'); + $returncode = $result['rc'] ?? 1; + $output = [ + $result['stdout'] ?? '' + ]; + } catch (\Exception $e) { + Log::error($e->getMessage()); + } + } else { + // Local running Naemon + $naemonBin = Configure::read('nagios.basepath') . Configure::read('nagios.bin') . Configure::read('nagios.nagios_bin'); + $naemonCfg = Configure::read('nagios.nagios_cfg'); + $cmd = sprintf( + 'sudo -u %s %s -v %s', + escapeshellarg($systemsettings['MONITORING']['MONITORING.USER']), + $naemonBin, + $naemonCfg + ); + $output = null; + exec($cmd, $output, $returncode); + } $verifyEntity->set('finished', 1); if ($returncode === 0) { //New configuration is valid :-) @@ -1350,85 +1416,133 @@ public function launchExport($createBackup = 1) { $ExportsTable->save($verifyEntity); //Reloading the monitoring system - - //Check if Naemon/Nagios is running. - //If Nagios is running, we reload the config, if not we need to restart - $entity = $ExportsTable->newEntity([ - 'task' => 'is_monitoring_engine_running', - 'text' => __('Check if monitoring engine is running') - ]); - $ExportsTable->save($entity); - exec($systemsettings['MONITORING']['MONITORING.STATUS'], $statusOutput, $statusRc); - $entity->set('finished', 1); - $entity->set('successfully', 1); - $ExportsTable->save($entity); - unset($entity); - - $isMonitoringRunning = false; - if ($statusRc === 0) { - //Nagios/Naemon is running (reload) + if (IS_CONTAINER === false) { + //Check if Naemon/Nagios is running. + //If Nagios is running, we reload the config, if not we need to restart $entity = $ExportsTable->newEntity([ - 'task' => 'export_reload_monitoring', - 'text' => __('Reloading monitoring engine') + 'task' => 'is_monitoring_engine_running', + 'text' => __('Check if monitoring engine is running') ]); $ExportsTable->save($entity); - exec($systemsettings['MONITORING']['MONITORING.RELOAD'], $reloadOutput, $reloadRc); + exec($systemsettings['MONITORING']['MONITORING.STATUS'], $statusOutput, $statusRc); $entity->set('finished', 1); - $entity->set('successfully', 0); - if ($reloadRc === 0) { - $entity->set('successfully', 1); - $isMonitoringRunning = true; - } + $entity->set('successfully', 1); $ExportsTable->save($entity); unset($entity); + + $isMonitoringRunning = false; + if ($statusRc === 0) { + //Nagios/Naemon is running (reload) + $entity = $ExportsTable->newEntity([ + 'task' => 'export_reload_monitoring', + 'text' => __('Reloading monitoring engine') + ]); + $ExportsTable->save($entity); + exec($systemsettings['MONITORING']['MONITORING.RELOAD'], $reloadOutput, $reloadRc); + $entity->set('finished', 1); + $entity->set('successfully', 0); + if ($reloadRc === 0) { + $entity->set('successfully', 1); + $isMonitoringRunning = true; + } + $ExportsTable->save($entity); + unset($entity); + } else { + //Nagios/Naemon is stopped (restart) + $entity = $ExportsTable->newEntity([ + 'task' => 'export_restart_monitoring', + 'text' => __('Restarting monitoring engine') + ]); + $ExportsTable->save($entity); + exec($systemsettings['MONITORING']['MONITORING.RESTART'], $restartOutput, $restartRc); + $entity->set('finished', 1); + $entity->set('successfully', 0); + if ($restartRc === 0) { + $entity->set('successfully', 1); + $isMonitoringRunning = true; + } + $ExportsTable->save($entity); + unset($entity); + } + + + if ($isMonitoringRunning) { + //Run After Export command + $entity = $ExportsTable->newEntity([ + 'task' => 'export_after_export_command', + 'text' => __('Execute after export command') + ]); + $output = null; + exec($systemsettings['MONITORING']['MONITORING.AFTER_EXPORT'], $output, $returncode); + + // Avoid "MySQL server has gone away" + $connection = $SystemsettingsTable->getConnection(); + $connection->disconnect(); + $connection->connect(); + + $entity->set('finished', 1); + $entity->set('successfully', 0); + if ($returncode == 0) { + $entity->set('successfully', 1); + $this->distributedMonitoringAfterExportCommand(); + $this->importModuleAfterExportCommand(); + } else { + $successfully = 0; + } + + $ExportsTable->save($entity); + unset($entity); + + } else { + $successfully = 0; + } } else { - //Nagios/Naemon is stopped (restart) + // Containerized openITCOCKPIT + // Restart remote Naemon $entity = $ExportsTable->newEntity([ 'task' => 'export_restart_monitoring', - 'text' => __('Restarting monitoring engine') + 'text' => __('Restarting containerized monitoring engine') ]); $ExportsTable->save($entity); - exec($systemsettings['MONITORING']['MONITORING.RESTART'], $restartOutput, $restartRc); + $result = $Supervisorctl->restart('naemon'); $entity->set('finished', 1); $entity->set('successfully', 0); - if ($restartRc === 0) { + if ($result === true) { $entity->set('successfully', 1); $isMonitoringRunning = true; } $ExportsTable->save($entity); - unset($entity); - } + if ($isMonitoringRunning) { + //Run After Export command + $entity = $ExportsTable->newEntity([ + 'task' => 'export_after_export_command', + 'text' => __('Execute after export command') + ]); + $output = null; + exec($systemsettings['MONITORING']['MONITORING.AFTER_EXPORT'], $output, $returncode); - if ($isMonitoringRunning) { - //Run After Export command - $entity = $ExportsTable->newEntity([ - 'task' => 'export_after_export_command', - 'text' => __('Execute after export command') - ]); - $output = null; - exec($systemsettings['MONITORING']['MONITORING.AFTER_EXPORT'], $output, $returncode); + // Avoid "MySQL server has gone away" + $connection = $SystemsettingsTable->getConnection(); + $connection->disconnect(); + $connection->connect(); - // Avoid "MySQL server has gone away" - $connection = $SystemsettingsTable->getConnection(); - $connection->disconnect(); - $connection->connect(); + $entity->set('finished', 1); + $entity->set('successfully', 0); + if ($returncode == 0) { + $entity->set('successfully', 1); + $this->distributedMonitoringAfterExportCommand(); + $this->importModuleAfterExportCommand(); + } else { + $successfully = 0; + } + + $ExportsTable->save($entity); + unset($entity); - $entity->set('finished', 1); - $entity->set('successfully', 0); - if ($returncode == 0) { - $entity->set('successfully', 1); - $this->distributedMonitoringAfterExportCommand(); - $this->importModuleAfterExportCommand(); } else { $successfully = 0; } - - $ExportsTable->save($entity); - unset($entity); - - } else { - $successfully = 0; } } else { //Error with new configuration :-( diff --git a/src/Command/SetupCommand.php b/src/Command/SetupCommand.php index 31db1dc224..da37ac1e43 100644 --- a/src/Command/SetupCommand.php +++ b/src/Command/SetupCommand.php @@ -37,6 +37,7 @@ use Cake\ORM\TableRegistry; use Cake\Validation\Validation; use itnovum\openITCOCKPIT\Core\DbBackend; +use itnovum\openITCOCKPIT\Core\System\Health\SystemId; use itnovum\openITCOCKPIT\SetupShell\MailConfigurator; use itnovum\openITCOCKPIT\SetupShell\MailConfigValue; use itnovum\openITCOCKPIT\SetupShell\MailConfigValueInt; @@ -167,6 +168,8 @@ public function execute(Arguments $args, ConsoleIo $io) { } + // Generate new SystemId + $SystemId = new SystemId(); $this->io->out(''); $this->io->hr(); $this->io->success(__('You can now open the web frontend in your browser and login. Have a nice day!')); diff --git a/src/Command/SupervisorCommand.php b/src/Command/SupervisorCommand.php new file mode 100644 index 0000000000..42f2d7d2c5 --- /dev/null +++ b/src/Command/SupervisorCommand.php @@ -0,0 +1,168 @@ + +// +// This file is dual licensed +// +// 1. +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +// 2. +// If you purchased an openITCOCKPIT Enterprise Edition you can use this file +// under the terms of the openITCOCKPIT Enterprise Edition license agreement. +// License agreement and license key will be shipped with the order +// confirmation. + +declare(strict_types=1); + +namespace App\Command; + +use App\itnovum\openITCOCKPIT\Supervisor\Supervisorctl; +use Cake\Command\Command; +use Cake\Console\Arguments; +use Cake\Console\ConsoleIo; +use Cake\Console\ConsoleOptionParser; + +/** + * Supervisor command. + */ +class SupervisorCommand extends Command { + /** + * Hook method for defining this command's option parser. + * + * @see https://book.cakephp.org/4/en/console-commands/commands.html#defining-arguments-and-options + * @param \Cake\Console\ConsoleOptionParser $parser The parser to be defined + * @return \Cake\Console\ConsoleOptionParser The built parser. + */ + public function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser { + $parser = parent::buildOptionParser($parser); + + $parser->addArgument('command', [ + 'help' => 'Command to execute', + 'required' => true, + 'choices' => ['start', 'stop', 'restart', 'status'], + ]); + + $parser->addArgument('service_name', [ + 'help' => 'Command to execute', + 'required' => true, + 'choices' => [ + // If you edit this like, also make sure to edit + // Supervisorctl::getSupervisorApiEndpointByServiceName() + + // openITCOCKPIT background + 'oitc_cmd', + 'sudo_server', + 'gearman_worker', + 'event-collectd', //todo + 'push_notification', + 'prometheus_bridge', //todo + 'customalert_worker', //todo + + // Monitoring Engines + 'naemon', + 'prometheus', + 'nsta', + + // Statusengine + 'statusengine', + + // puppeteer / PDF generation + 'openitcockpit-node', + + // System + 'nginx', + 'php-fpm', + 'snmptrapd', + 'snmptt', + ], + ]); + + return $parser; + } + + /** + * Implement this method with your command's logic. + * + * @param \Cake\Console\Arguments $args The command arguments. + * @param \Cake\Console\ConsoleIo $io The console io + * @return null|void|int The exit code or null for success + */ + public function execute(Arguments $args, ConsoleIo $io) { + $command = $args->getArgument('command'); + $serviceName = $args->getArgument('service_name'); + + $Supervisorctl = new Supervisorctl(); + + switch ($command) { + case 'start': + $result = $Supervisorctl->start($serviceName); + if ($result === true) { + $io->out(sprintf('Service %s started ', $serviceName), 0); + $io->success('successfully.', 1); + break; + } + + print_r($result); + break; + + case 'stop': + $result = $Supervisorctl->stop($serviceName); + if ($result === true) { + $io->out(sprintf('Service %s stopped ', $serviceName), 0); + $io->success('successfully.', 1); + break; + } + + print_r($result); + break; + + case 'restart': + $result = $Supervisorctl->stop($serviceName); + if ($result === true) { + $io->out(sprintf('Service %s stopped ', $serviceName), 0); + $io->success('successfully.', 1); + } else { + print_r($result); + } + + $result = $Supervisorctl->start($serviceName); + if ($result === true) { + $io->out(sprintf('Service %s started ', $serviceName), 0); + $io->success('successfully.', 1); + } else { + print_r($result); + } + break; + + default: + $result = $Supervisorctl->status($serviceName); + if ($result['statename'] === 'RUNNING') { + $io->out(sprintf('Service %s is ', $serviceName), 0); + $io->success('running. ', 0); + $io->out($result['description']); + break; + } + + if ($result['statename'] === 'STOPPED') { + $io->out(sprintf('Service %s is ', $serviceName), 0); + $io->error('stopped.'); + break; + } + + // Unknown state + print_r($result); + } + + } +} diff --git a/src/Command/SystemHealthCommand.php b/src/Command/SystemHealthCommand.php index 87786653b2..3f441c72d3 100644 --- a/src/Command/SystemHealthCommand.php +++ b/src/Command/SystemHealthCommand.php @@ -27,6 +27,7 @@ namespace App\Command; +use App\itnovum\openITCOCKPIT\Supervisor\Supervisorctl; use App\Model\Table\SystemsettingsTable; use Cake\Console\Arguments; use Cake\Console\Command; @@ -95,6 +96,27 @@ public function fetchInformation() { 'isNodeJsServerRunning' => false ]; + if (IS_CONTAINER) { + // openITCOCKPIT is running inside a container like docker + $Supervisorctl = new Supervisorctl(); + $data = [ + 'isNagiosRunning' => $Supervisorctl->isRunning('naemon'), + 'isNdoRunning' => false, + 'isStatusengineRunning' => $Supervisorctl->isRunning('statusengine'), + 'isNpcdRunning' => false, + 'isOitcCmdRunning' => $Supervisorctl->isRunning('oitc_cmd'), + 'isSudoServerRunning' => $Supervisorctl->isRunning('sudo_server'), + 'isNstaRunning' => $Supervisorctl->isRunning('nsta'), + 'isGearmanWorkerRunning' => $Supervisorctl->isRunning('gearman_worker'), + 'isNdoInstalled' => false, + 'isStatusengineInstalled' => true, //NDOUtils are not supported anymore + 'isStatusenginePerfdataProcessor' => true, //NPCD is not supported anymore + 'isDistributeModuleInstalled' => false, + 'isPushNotificationRunning' => $Supervisorctl->isRunning('push_notification'), + 'isNodeJsServerRunning' => $Supervisorctl->isRunning('openitcockpit-node') + ]; + } + /** @var SystemsettingsTable $SystemsettingsTable */ $SystemsettingsTable = TableRegistry::getTableLocator()->get('Systemsettings'); $systemsetting = $SystemsettingsTable->findAsArray(); @@ -133,70 +155,72 @@ public function fetchInformation() { $errorRedirect = ' 2> /dev/null'; - - exec($systemsetting['MONITORING']['MONITORING.STATUS'] . $errorRedirect, $output, $returncode); - if ($returncode == 0) { - $data['isNagiosRunning'] = true; - } - - exec($systemsetting['INIT']['INIT.NDO_STATUS'] . $errorRedirect, $output, $returncode); - if ($returncode == 0) { - $data['isNdoRunning'] = true; - } - - exec($systemsetting['INIT']['INIT.STATUSENIGNE_STATUS'] . $errorRedirect, $output, $returncode); - if ($returncode == 0) { - $data['isStatusengineRunning'] = true; - } - - exec($systemsetting['INIT']['INIT.NPCD_STATUS'] . $errorRedirect, $output, $returncode); - if ($returncode == 0) { - $data['isNpcdRunning'] = true; - } - - exec($systemsetting['INIT']['INIT.OITC_CMD_STATUS'] . $errorRedirect, $output, $returncode); - if ($returncode == 0) { - $data['isOitcCmdRunning'] = true; - } - - exec($systemsetting['INIT']['INIT.SUDO_SERVER_STATUS'] . $errorRedirect, $output, $returncode); - if ($returncode == 0) { - $data['isSudoServerRunning'] = true; + if (IS_CONTAINER === false) { + // Normal installation of openITCOCKPIT via apt, dnf or git + exec($systemsetting['MONITORING']['MONITORING.STATUS'] . $errorRedirect, $output, $returncode); + if ($returncode == 0) { + $data['isNagiosRunning'] = true; + } + + exec($systemsetting['INIT']['INIT.NDO_STATUS'] . $errorRedirect, $output, $returncode); + if ($returncode == 0) { + $data['isNdoRunning'] = true; + } + + exec($systemsetting['INIT']['INIT.STATUSENIGNE_STATUS'] . $errorRedirect, $output, $returncode); + if ($returncode == 0) { + $data['isStatusengineRunning'] = true; + } + + exec($systemsetting['INIT']['INIT.NPCD_STATUS'] . $errorRedirect, $output, $returncode); + if ($returncode == 0) { + $data['isNpcdRunning'] = true; + } + + exec($systemsetting['INIT']['INIT.OITC_CMD_STATUS'] . $errorRedirect, $output, $returncode); + if ($returncode == 0) { + $data['isOitcCmdRunning'] = true; + } + + exec($systemsetting['INIT']['INIT.SUDO_SERVER_STATUS'] . $errorRedirect, $output, $returncode); + if ($returncode == 0) { + $data['isSudoServerRunning'] = true; + } + + exec($systemsetting['INIT']['INIT.NSTA_STATUS'] . $errorRedirect, $output, $returncode); + if ($returncode == 0) { + $data['isNstaRunning'] = true; + } + + exec($systemsetting['INIT']['INIT.PUSH_NOTIFICATION'] . $errorRedirect, $output, $returncode); + if ($returncode == 0) { + $data['isPushNotificationRunning'] = true; + } + + exec($systemsetting['INIT']['INIT.OPENITCOCKPIT_NODE'] . $errorRedirect, $output, $returncode); + if ($returncode == 0) { + $data['isNodeJsServerRunning'] = true; + } + + if (file_exists('/opt/openitc/nagios/bin/ndo2db')) { + $data['isNdoInstalled'] = true; + } + + //if (file_exists('/opt/openitc/statusengine2/cakephp/app/Console/Command/StatusengineLegacyShell.php')) { + // $data['isStatusengineInstalled'] = true; + //} + + //$statusengineConfig = '/opt/openitc/statusengine2/cakephp/app/Config/Statusengine.php'; + //if (file_exists($statusengineConfig)) { + // require_once $statusengineConfig; + // if (isset($config['process_perfdata'])) { + // if ($config['process_perfdata'] === true) { + // $data['isStatusenginePerfdataProcessor'] = true; + // } + // } + //} } - exec($systemsetting['INIT']['INIT.NSTA_STATUS'] . $errorRedirect, $output, $returncode); - if ($returncode == 0) { - $data['isNstaRunning'] = true; - } - - exec($systemsetting['INIT']['INIT.PUSH_NOTIFICATION'] . $errorRedirect, $output, $returncode); - if ($returncode == 0) { - $data['isPushNotificationRunning'] = true; - } - - exec($systemsetting['INIT']['INIT.OPENITCOCKPIT_NODE'] . $errorRedirect, $output, $returncode); - if ($returncode == 0) { - $data['isNodeJsServerRunning'] = true; - } - - if (file_exists('/opt/openitc/nagios/bin/ndo2db')) { - $data['isNdoInstalled'] = true; - } - - //if (file_exists('/opt/openitc/statusengine2/cakephp/app/Console/Command/StatusengineLegacyShell.php')) { - // $data['isStatusengineInstalled'] = true; - //} - - //$statusengineConfig = '/opt/openitc/statusengine2/cakephp/app/Config/Statusengine.php'; - //if (file_exists($statusengineConfig)) { - // require_once $statusengineConfig; - // if (isset($config['process_perfdata'])) { - // if ($config['process_perfdata'] === true) { - // $data['isStatusenginePerfdataProcessor'] = true; - // } - // } - //} - if (Plugin::isLoaded('DistributeModule')) { $data['isDistributeModuleInstalled'] = true; } @@ -206,8 +230,12 @@ public function fetchInformation() { public function saveToCache($data) { $data['update'] = time(); + + $redisHost = env('OITC_REDIS_HOST', '127.0.0.1'); + $redisPort = filter_var(env('OITC_REDIS_PORT', 6379), FILTER_VALIDATE_INT); + $Redis = new \Redis(); - $Redis->connect('127.0.0.1', 6379); + $Redis->connect($redisHost, $redisPort); $Redis->setex('permissions_system_health', 60 * 3, serialize($data)); } } diff --git a/src/Controller/AdministratorsController.php b/src/Controller/AdministratorsController.php index 25263150c7..6fc4c1cba4 100644 --- a/src/Controller/AdministratorsController.php +++ b/src/Controller/AdministratorsController.php @@ -127,6 +127,15 @@ function debug() { //NPCD is not supported anymore! $isStatusenginePerfdataProcessor = true; + if (IS_CONTAINER) { + // We are running inside a container and are therefore a recent version of openITCOCKPIT. + // NDO and NPCD support was dropped with openITCOCKPIT 3.x (at least 2 years before container support) + // so there is absolutely no way we have one of those two installed. + // Honestly speaking the checking for NDO and NPCD should be removed anyway! + $isStatusengineInstalled = true; + $isStatusenginePerfdataProcessor = true; + } + $processInformation = [ 'gearmanReachable' => $gearmanReachable, 'isGearmanWorkerRunning' => $isGearmanWorkerRunning, @@ -139,9 +148,9 @@ function debug() { //Collect server information $LsbRelease = new LsbRelease(); - if($LsbRelease->isDebianBased()){ + if ($LsbRelease->isDebianBased()) { $osVersion = sprintf('%s %s (%s)', $LsbRelease->getVendor(), $LsbRelease->getVersion(), $LsbRelease->getCodename()); - }else{ + } else { $osVersion = $LsbRelease->getCodename(); } @@ -159,7 +168,8 @@ function debug() { 'php_version' => PHP_VERSION, 'php_memory_limit' => str_replace('M', '', get_cfg_var('memory_limit')) . 'MB', 'php_max_execution_time' => ini_get('max_execution_time'), - 'php_extensions' => get_loaded_extensions() + 'php_extensions' => get_loaded_extensions(), + 'containerized' => (IS_CONTAINER) ? __('Yes') : __('No') ]; //Collect CPU load history @@ -200,7 +210,7 @@ function debug() { $gearmanStatus = []; $output = null; if ($gearmanReachable) { - exec('gearadmin --status', $output); + exec('gearadmin --status -h ' . escapeshellarg(env('OITC_GEARMAN_ADDRESS', 'localhost')), $output); //Parse output $trash = array_pop($output); foreach ($output as $line) { @@ -357,8 +367,8 @@ public function testMail() { $Mailer->setEmailFormat('both'); $Mailer->setAttachments([ 'logo.png' => [ - 'file' => $Logo->getSmallLogoDiskPath(), - 'mimetype' => 'image/png', + 'file' => $Logo->getSmallLogoDiskPath(), + 'mimetype' => 'image/png', 'contentId' => '100' ] ]); diff --git a/src/Controller/AngularController.php b/src/Controller/AngularController.php index 307ef29209..f926bbdd8a 100644 --- a/src/Controller/AngularController.php +++ b/src/Controller/AngularController.php @@ -1003,8 +1003,9 @@ public function queryhandler() { $QueryHandler = new QueryHandler($SystemsettingsTable->getQueryHandlerPath()); $this->set('QueryHandler', [ - 'exists' => $QueryHandler->exists(), - 'path' => $QueryHandler->getPath() + 'exists' => $QueryHandler->exists(), + 'path' => $QueryHandler->getPath(), + 'isContainer' => $QueryHandler->isContainer() ]); $this->viewBuilder()->setOption('serialize', ['QueryHandler']); @@ -1258,6 +1259,7 @@ public function regexHelperTooltip() { //Return HTML Template for PaginatorDirective return; } + public function ackTooltip() { //Only ship HTML template return; diff --git a/src/Controller/ConfigurationFilesController.php b/src/Controller/ConfigurationFilesController.php index 972c122c80..a0168da13c 100644 --- a/src/Controller/ConfigurationFilesController.php +++ b/src/Controller/ConfigurationFilesController.php @@ -32,6 +32,7 @@ use Cake\Http\Exception\ForbiddenException; use Cake\Http\Exception\MethodNotAllowedException; use Cake\Http\Exception\NotFoundException; +use Cake\Http\Exception\ServiceUnavailableException; use Cake\ORM\TableRegistry; use itnovum\openITCOCKPIT\ConfigGenerator\ConfigInterface; use itnovum\openITCOCKPIT\ConfigGenerator\GeneratorRegistry; @@ -49,22 +50,24 @@ public function index() { } $configFilesForFrontend = []; - $GeneratorRegistry = new GeneratorRegistry(); - foreach ($GeneratorRegistry->getAllConfigFilesWithCategory() as $categoryName => $ConfigFileObjects) { - $category = [ - 'name' => $categoryName, - 'configFiles' => [] - ]; - - foreach ($ConfigFileObjects as $ConfigFileObject) { - /** @var ConfigInterface $ConfigFileObject */ - $category['configFiles'][] = [ - 'linkedOutfile' => $ConfigFileObject->getLinkedOutfile(), - 'dbKey' => $ConfigFileObject->getDbKey() + if (IS_CONTAINER === false) { + $GeneratorRegistry = new GeneratorRegistry(); + foreach ($GeneratorRegistry->getAllConfigFilesWithCategory() as $categoryName => $ConfigFileObjects) { + $category = [ + 'name' => $categoryName, + 'configFiles' => [] ]; - } - $configFilesForFrontend[] = $category; + foreach ($ConfigFileObjects as $ConfigFileObject) { + /** @var ConfigInterface $ConfigFileObject */ + $category['configFiles'][] = [ + 'linkedOutfile' => $ConfigFileObject->getLinkedOutfile(), + 'dbKey' => $ConfigFileObject->getDbKey() + ]; + } + + $configFilesForFrontend[] = $category; + } } $this->set('configFileCategories', $configFilesForFrontend); @@ -80,6 +83,10 @@ public function edit($configFile = null) { return; } + if (IS_CONTAINER) { + throw new ServiceUnavailableException('Containerized installations are configured through environment variables'); + } + $GeneratorRegistry = new GeneratorRegistry(); foreach ($GeneratorRegistry->getAllConfigFiles() as $ConfigFileObject) { @@ -194,6 +201,10 @@ public function restorDefault($configFile) { throw new MethodNotAllowedException(); } + if (IS_CONTAINER) { + throw new ServiceUnavailableException('Containerized installations are configured through environment variables'); + } + $className = sprintf('itnovum\openITCOCKPIT\ConfigGenerator\%s', $configFile); if (!class_exists($className)) { throw new NotFoundException('Config file not found'); @@ -252,6 +263,10 @@ private function __sharedControllerAction($ConfigurationObjectClassName, $ShortC /** @var ConfigurationFilesTable $ConfigurationFilesTable */ $ConfigurationFilesTable = TableRegistry::getTableLocator()->get('ConfigurationFiles'); + if (IS_CONTAINER) { + throw new ServiceUnavailableException('Containerized installations are configured through environment variables'); + } + if ($this->request->is('get') && $this->isAngularJsRequest()) { $dbConfig = $ConfigurationFilesTable->getConfigValuesByConfigFile($ConfigurationObjectClassName->getDbKey()); $config = $ConfigurationObjectClassName->mergeDbResultWithDefaultConfiguration($dbConfig); diff --git a/src/Controller/PacketmanagerController.php b/src/Controller/PacketmanagerController.php index da54e7281f..f8673b3c0c 100644 --- a/src/Controller/PacketmanagerController.php +++ b/src/Controller/PacketmanagerController.php @@ -91,26 +91,35 @@ public function index() { $installedModules = []; $output = []; - $LsbRelease = new LsbRelease(); - if ($LsbRelease->isDebianBased()) { - exec('dpkg -l |grep openitcockpit-module', $output, $rc); - //$output = $this->getTestDpkgOutput(); - } - - if ($LsbRelease->isRhelBased()) { - exec('dnf list installed | grep openitcockpit-module', $output, $rc); - //$output = $this->getTestDnfOutput(); - } + if (IS_CONTAINER === false) { + $LsbRelease = new LsbRelease(); + if ($LsbRelease->isDebianBased()) { + exec('dpkg -l |grep openitcockpit-module', $output, $rc); + //$output = $this->getTestDpkgOutput(); + } - foreach ($output as $line) { - preg_match_all('/(openitcockpit\-module\-\w+)/', $line, $matches); - if (isset($matches[0][0])) { - $module = $matches[0][0]; - $installedModules[$module] = true; + if ($LsbRelease->isRhelBased()) { + exec('dnf list installed | grep openitcockpit-module', $output, $rc); + //$output = $this->getTestDnfOutput(); } - if (isset($matches)) { - unset($matches); + foreach ($output as $line) { + preg_match_all('/(openitcockpit\-module\-\w+)/', $line, $matches); + if (isset($matches[0][0])) { + $module = $matches[0][0]; + $installedModules[$module] = true; + } + + if (isset($matches)) { + unset($matches); + } + } + } else { + // Container based version of openITCOCKPIT have always all modules enabled + if (isset($result['data']['modules'])) { + foreach ($result['data']['modules'] as $module) { + $installedModules[$module['Module']['apt_name']] = true; + } } } diff --git a/src/Template/Administrators/debug.php b/src/Template/Administrators/debug.php index efaeed16f7..0aeb56e628 100755 --- a/src/Template/Administrators/debug.php +++ b/src/Template/Administrators/debug.php @@ -77,35 +77,36 @@ - -element('repository_checker'); ?> - -getCodename() === 'bionic'): ?> -
- × -

- - -

- - -
- support@itsm.it-novum.com'); ?> -
- - -getCodename() === 'buster'): ?> -
- × -

- - -

- - -
- support@itsm.it-novum.com'); ?> -
+ + element('repository_checker'); ?> + + getCodename() === 'bionic'): ?> +
+ × +

+ + +

+ + +
+ support@itsm.it-novum.com'); ?> +
+ + + getCodename() === 'buster'): ?> +
+ × +

+ + +

+ + +
+ support@itsm.it-novum.com'); ?> +
+
@@ -122,7 +123,8 @@
- Logo + Logo
@@ -431,6 +433,9 @@
:
{{serverInformation.kernel}}
+
:
+
{{serverInformation.containerized}}
+
:
{{serverInformation.php_version}}
diff --git a/src/Template/Angular/queryhandler.php b/src/Template/Angular/queryhandler.php index 6f4bd5b032..b0bb219a4d 100644 --- a/src/Template/Angular/queryhandler.php +++ b/src/Template/Angular/queryhandler.php @@ -1,4 +1,4 @@ -
+

diff --git a/src/Template/ConfigurationFiles/index.php b/src/Template/ConfigurationFiles/index.php index 37e701eb61..9fe498675e 100644 --- a/src/Template/ConfigurationFiles/index.php +++ b/src/Template/ConfigurationFiles/index.php @@ -50,6 +50,29 @@
+ + + +
+
+
+ + + + +
+
+ + + +
+ +
+ +
+
+ +
diff --git a/src/Template/Packetmanager/index.php b/src/Template/Packetmanager/index.php index 527e04e170..1a125a80bd 100755 --- a/src/Template/Packetmanager/index.php +++ b/src/Template/Packetmanager/index.php @@ -49,34 +49,57 @@ + + + element('repository_checker'); ?> + + getCodename() === 'bionic'): ?> +
+ × +

+ + +

+ + +
+ support@itsm.it-novum.com'); ?> +
+ + + getCodename() === 'buster'): ?> +
+ × +

+ + +

+ + +
+ support@itsm.it-novum.com'); ?> +
+ + + +
+
+
+ + + + +
+
+ + + +
+ + +
-element('repository_checker'); ?> - -getCodename() === 'bionic'): ?> -
- × -

- - -

- - -
- support@itsm.it-novum.com'); ?> -
- - -getCodename() === 'buster'): ?> -
- × -

- - -

- - -
- support@itsm.it-novum.com'); ?> +
diff --git a/src/Template/Statistics/index.php b/src/Template/Statistics/index.php index 77cb0f286a..9bd3ab25b0 100644 --- a/src/Template/Statistics/index.php +++ b/src/Template/Statistics/index.php @@ -132,7 +132,11 @@
- /opt/openitc/etc/system-id. + + /opt/openitc/var/system-id + + /opt/openitc/etc/system-id. +
diff --git a/src/config_templates/PhpFpmOitc/oitc.conf.tpl b/src/config_templates/PhpFpmOitc/oitc.conf.tpl index 42ef8bfbb2..1e6834bc2a 100644 --- a/src/config_templates/PhpFpmOitc/oitc.conf.tpl +++ b/src/config_templates/PhpFpmOitc/oitc.conf.tpl @@ -375,7 +375,11 @@ pm.max_spare_servers = {{max_spare_servers}} ; Setting to "no" will make all environment variables available to PHP code ; via getenv(), $_ENV and $_SERVER. ; Default Value: yes +{% if IS_CONTAINER %} +clear_env = no +{% else %} ;clear_env = no +{% endif %} ; Limits the extensions of the main script FPM will allow to parse. This can ; prevent configuration mistakes on the web server side. You should only limit diff --git a/src/itnovum/openITCOCKPIT/ConfigGenerator/AfterExport.php b/src/itnovum/openITCOCKPIT/ConfigGenerator/AfterExport.php index ceda60280f..95be86342b 100644 --- a/src/itnovum/openITCOCKPIT/ConfigGenerator/AfterExport.php +++ b/src/itnovum/openITCOCKPIT/ConfigGenerator/AfterExport.php @@ -25,9 +25,10 @@ namespace itnovum\openITCOCKPIT\ConfigGenerator; +use App\itnovum\openITCOCKPIT\ConfigGenerator\ContainerConfigInterface; use Cake\Core\Configure; -class AfterExport extends ConfigGenerator implements ConfigInterface { +class AfterExport extends ConfigGenerator implements ConfigInterface, ContainerConfigInterface { protected $templateDir = 'config'; @@ -91,6 +92,31 @@ public function getHelpText($key) { return ''; } + public function getValuesFromEnvironment() { + return [ + [ + 'key' => 'username', + 'value' => env('OITC_AFTER_EXPORT_SSH_USERNAME', 'nagios'), + ], + [ + 'key' => 'private_key', + 'value' => env('OITC_AFTER_EXPORT_SSH_PRIVATE_KEY', '/var/lib/nagios/.ssh/id_rsa'), + ], + [ + 'key' => 'public_key', + 'value' => env('OITC_AFTER_EXPORT_SSH_PUBLIC_KEY', '/var/lib/nagios/.ssh/id_rsa.pub'), + ], + [ + 'key' => 'restart_command', + 'value' => env('OITC_AFTER_EXPORT_RESTART_COMMAND', 'sudo /opt/openitc/nagios/bin/restart-monitoring.sh'), + ], + [ + 'key' => 'remote_port', + 'value' => env('OITC_AFTER_EXPORT_SSH_REMOTE_PORT', 22), + ] + ]; + } + /** * Save the configuration as text file on disk * diff --git a/src/itnovum/openITCOCKPIT/ConfigGenerator/ConfigGenerator.php b/src/itnovum/openITCOCKPIT/ConfigGenerator/ConfigGenerator.php index e01b005589..b2760160fa 100644 --- a/src/itnovum/openITCOCKPIT/ConfigGenerator/ConfigGenerator.php +++ b/src/itnovum/openITCOCKPIT/ConfigGenerator/ConfigGenerator.php @@ -300,6 +300,13 @@ public function mergeDbResultWithDefaultConfiguration($dbRecords) { private function flatDbResult($dbResult) { $result = []; foreach ($dbResult as $record) { + // ITC-2986 make default settings from environment variable look like from the database + if(!isset($record['ConfigurationFile'])){ + $record = [ + 'ConfigurationFile' => $record + ]; + } + $result[$record['ConfigurationFile']['key']] = $record['ConfigurationFile']['value']; } return $result; @@ -344,6 +351,7 @@ protected function saveConfigFile($configToExport) { $FileHeader = new FileHeader(); $configToExport['STATIC_FILE_HEADER'] = $FileHeader->getHeader($this->commentChar); + $configToExport['IS_CONTAINER'] = IS_CONTAINER; $configDir = dirname($this->realOutfile); if (!is_dir($configDir)) { diff --git a/src/itnovum/openITCOCKPIT/ConfigGenerator/ConfigInterface.php b/src/itnovum/openITCOCKPIT/ConfigGenerator/ConfigInterface.php index f181edc592..19fead003b 100644 --- a/src/itnovum/openITCOCKPIT/ConfigGenerator/ConfigInterface.php +++ b/src/itnovum/openITCOCKPIT/ConfigGenerator/ConfigInterface.php @@ -72,4 +72,4 @@ public function getDbKey(); */ public function migrate($dbRecords); -} \ No newline at end of file +} diff --git a/src/itnovum/openITCOCKPIT/ConfigGenerator/ContainerConfigInterface.php b/src/itnovum/openITCOCKPIT/ConfigGenerator/ContainerConfigInterface.php new file mode 100644 index 0000000000..c916a29ed1 --- /dev/null +++ b/src/itnovum/openITCOCKPIT/ConfigGenerator/ContainerConfigInterface.php @@ -0,0 +1,35 @@ + +// +// This file is dual licensed +// +// 1. +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// 2. +// If you purchased an openITCOCKPIT Enterprise Edition you can use this file +// under the terms of the openITCOCKPIT Enterprise Edition license agreement. +// License agreement and license key will be shipped with the order +// confirmation. + +namespace App\itnovum\openITCOCKPIT\ConfigGenerator; + + +interface ContainerConfigInterface { + + /** + * @return array + */ + public function getValuesFromEnvironment(); + +} diff --git a/src/itnovum/openITCOCKPIT/ConfigGenerator/DbBackend.php b/src/itnovum/openITCOCKPIT/ConfigGenerator/DbBackend.php index 2b4b5ddf2c..f8af3a30ea 100644 --- a/src/itnovum/openITCOCKPIT/ConfigGenerator/DbBackend.php +++ b/src/itnovum/openITCOCKPIT/ConfigGenerator/DbBackend.php @@ -25,9 +25,10 @@ namespace itnovum\openITCOCKPIT\ConfigGenerator; +use App\itnovum\openITCOCKPIT\ConfigGenerator\ContainerConfigInterface; use Cake\Core\Configure; -class DbBackend extends ConfigGenerator implements ConfigInterface { +class DbBackend extends ConfigGenerator implements ConfigInterface, ContainerConfigInterface { protected $templateDir = 'config'; @@ -104,6 +105,15 @@ public function getHelpText($key) { return ''; } + public function getValuesFromEnvironment() { + return [ + [ + 'key' => 'dbbackend', + 'value' => env('OITC_DB_BACKEND', 'Statusengine3'), + ] + ]; + } + /** * Save the configuration as text file on disk * diff --git a/src/itnovum/openITCOCKPIT/ConfigGenerator/Gearman.php b/src/itnovum/openITCOCKPIT/ConfigGenerator/Gearman.php index a9ef80eba8..44050813fd 100644 --- a/src/itnovum/openITCOCKPIT/ConfigGenerator/Gearman.php +++ b/src/itnovum/openITCOCKPIT/ConfigGenerator/Gearman.php @@ -4,9 +4,10 @@ namespace itnovum\openITCOCKPIT\ConfigGenerator; +use App\itnovum\openITCOCKPIT\ConfigGenerator\ContainerConfigInterface; use Cake\Core\Configure; -class Gearman extends ConfigGenerator implements ConfigInterface { +class Gearman extends ConfigGenerator implements ConfigInterface, ContainerConfigInterface { /** @var string */ protected $templateDir = 'config'; /** @var string */ @@ -20,8 +21,8 @@ class Gearman extends ConfigGenerator implements ConfigInterface { /** @var array */ protected $defaults = [ 'string' => [ - 'address' => '127.0.0.1', - 'pidfile' => '/var/run/oitc_gearmanworker.pid', + 'address' => '127.0.0.1', + 'pidfile' => '/var/run/oitc_gearmanworker.pid', ], 'int' => [ 'port' => 4730, @@ -52,10 +53,10 @@ public function customValidationRules($data) { */ public function getHelpText($key) { $help = [ - 'address' => __('The host address where the gearman-job-server is running.'), - 'pidfile' => __('Process id file used by the gearman_worker.'), - 'port' => __('Port number of gearman-job-server.'), - 'worker' => __('Number of gearman_worker processes.') + 'address' => __('The host address where the gearman-job-server is running.'), + 'pidfile' => __('Process id file used by the gearman_worker.'), + 'port' => __('Port number of gearman-job-server.'), + 'worker' => __('Number of gearman_worker processes.') ]; if (isset($help[$key])) { @@ -65,6 +66,23 @@ public function getHelpText($key) { return ''; } + public function getValuesFromEnvironment() { + return [ + [ + 'key' => 'address', + 'value' => env('OITC_GEARMAN_ADDRESS', 'gearmand'), + ], + [ + 'key' => 'port', + 'value' => env('OITC_GEARMAN_PORT', 4730), + ], + [ + 'key' => 'worker', + 'value' => env('OITC_GEARMAM_WORKER', 5), + ] + ]; + } + /** * @param array $dbRecords * @return bool|int diff --git a/src/itnovum/openITCOCKPIT/ConfigGenerator/GeneratorRegistry.php b/src/itnovum/openITCOCKPIT/ConfigGenerator/GeneratorRegistry.php index 235a23500d..71b16c984e 100644 --- a/src/itnovum/openITCOCKPIT/ConfigGenerator/GeneratorRegistry.php +++ b/src/itnovum/openITCOCKPIT/ConfigGenerator/GeneratorRegistry.php @@ -33,6 +33,8 @@ class GeneratorRegistry { /** + * This function is used, when openITCOCKPIT is running on Bare-metal server / a Virtual Maschine and is + * installed via the package manager like apt or dnf * @return array */ public function getAllConfigFiles() { @@ -65,6 +67,27 @@ public function getAllConfigFiles() { return $configFiles; } + /** + * This function is used, when openITCOCKPIT is running inside a container like Docker, Podman etc. + * In this case, we don't need most of the config files, because the corresponding containers will generate + * configuration files (if necessary) by themselves. + * ITC-2986 + * @return array + */ + public function getAllConfigFilesForContainer(){ + $configFiles = [ + new AfterExport(), + new DbBackend(), + new PerfdataBackend(), + new Gearman(), + new GraphiteWeb(), + new NSTAMaster(), + new PhpFpmOitc() + ]; + + return $configFiles; + } + /** * @return array */ diff --git a/src/itnovum/openITCOCKPIT/ConfigGenerator/GraphiteWeb.php b/src/itnovum/openITCOCKPIT/ConfigGenerator/GraphiteWeb.php index 8e5eebd51b..fe3c46a807 100644 --- a/src/itnovum/openITCOCKPIT/ConfigGenerator/GraphiteWeb.php +++ b/src/itnovum/openITCOCKPIT/ConfigGenerator/GraphiteWeb.php @@ -25,7 +25,9 @@ namespace itnovum\openITCOCKPIT\ConfigGenerator; -class GraphiteWeb extends ConfigGenerator implements ConfigInterface { +use App\itnovum\openITCOCKPIT\ConfigGenerator\ContainerConfigInterface; + +class GraphiteWeb extends ConfigGenerator implements ConfigInterface,ContainerConfigInterface { protected $templateDir = 'config'; @@ -98,6 +100,31 @@ public function getHelpText($key) { return ''; } + public function getValuesFromEnvironment() { + return [ + [ + 'key' => 'graphite_web_host', + 'value' => env('OITC_GRAPHITE_WEB_ADDRESS', '127.0.0.1'), + ], + [ + 'key' => 'graphite_prefix', + 'value' => env('OITC_GRAPHITE_PREFIX', 'openitcockpit'), + ], + [ + 'key' => 'graphite_web_port', + 'value' => env('OITC_GRAPHITE_WEB_PORT', 8888), + ], + [ + 'key' => 'use_https', + 'value' => env('OITC_GRAPHITE_USE_HTTPS', 0), + ], + [ + 'key' => 'use_proxy', + 'value' => env('OITC_GRAPHITE_USE_PROXY', 0), + ] + ]; + } + /** * Save the configuration as text file on disk * diff --git a/src/itnovum/openITCOCKPIT/ConfigGenerator/NSTAMaster.php b/src/itnovum/openITCOCKPIT/ConfigGenerator/NSTAMaster.php index 76b89a2f2d..52d95d7a6f 100644 --- a/src/itnovum/openITCOCKPIT/ConfigGenerator/NSTAMaster.php +++ b/src/itnovum/openITCOCKPIT/ConfigGenerator/NSTAMaster.php @@ -25,6 +25,7 @@ namespace itnovum\openITCOCKPIT\ConfigGenerator; +use App\itnovum\openITCOCKPIT\ConfigGenerator\ContainerConfigInterface; use itnovum\openITCOCKPIT\Core\System\Health\MonitoringEngine; /** @@ -32,7 +33,7 @@ * NSTA written in Go by Johannes * @package itnovum\openITCOCKPIT\ConfigGenerator */ -class NSTAMaster extends ConfigGenerator implements ConfigInterface { +class NSTAMaster extends ConfigGenerator implements ConfigInterface,ContainerConfigInterface { protected $templateDir = 'NSTA'; @@ -96,6 +97,31 @@ public function getHelpText($key) { return ''; } + public function getValuesFromEnvironment() { + return [ + [ + 'key' => 'listen_http', + 'value' => env('OITC_NSTA_LISTEN_HTTP', '127.0.0.1:7473'), + ], + [ + 'key' => 'listen_https', + 'value' => env('OITC_NSTA_LISTEN_HTTPS', '127.0.0.1:7474'), + ], + [ + 'key' => 'tls_key', + 'value' => env('OITC_NSTA_TLS_KEY', '/etc/ssl/private/ssl-cert-snakeoil.key'), + ], + [ + 'key' => 'tls_cert', + 'value' => env('OITC_NSTA_TLS_CERT', '/etc/ssl/certs/ssl-cert-snakeoil.pem'), + ], + [ + 'key' => 'use_nginx_proxy', + 'value' => env('OITC_NSTA_USE_NGINX_PROXY', 1), + ] + ]; + } + /** * Save the configuration as text file on disk * diff --git a/src/itnovum/openITCOCKPIT/ConfigGenerator/PerfdataBackend.php b/src/itnovum/openITCOCKPIT/ConfigGenerator/PerfdataBackend.php index 98ed1a5aee..588566b5e0 100644 --- a/src/itnovum/openITCOCKPIT/ConfigGenerator/PerfdataBackend.php +++ b/src/itnovum/openITCOCKPIT/ConfigGenerator/PerfdataBackend.php @@ -25,9 +25,10 @@ namespace itnovum\openITCOCKPIT\ConfigGenerator; +use App\itnovum\openITCOCKPIT\ConfigGenerator\ContainerConfigInterface; use Cake\Core\Configure; -class PerfdataBackend extends ConfigGenerator implements ConfigInterface { +class PerfdataBackend extends ConfigGenerator implements ConfigInterface,ContainerConfigInterface { protected $templateDir = 'config'; @@ -105,6 +106,15 @@ public function getHelpText($key) { return ''; } + public function getValuesFromEnvironment() { + return [ + [ + 'key' => 'perfdatabackend', + 'value' => env('OITC_PERFDATA_BACKEND', 'Whisper'), + ] + ]; + } + /** * Save the configuration as text file on disk * diff --git a/src/itnovum/openITCOCKPIT/ConfigGenerator/PhpFpmOitc.php b/src/itnovum/openITCOCKPIT/ConfigGenerator/PhpFpmOitc.php index 7491d0cbb0..9e592313a8 100644 --- a/src/itnovum/openITCOCKPIT/ConfigGenerator/PhpFpmOitc.php +++ b/src/itnovum/openITCOCKPIT/ConfigGenerator/PhpFpmOitc.php @@ -4,9 +4,10 @@ namespace itnovum\openITCOCKPIT\ConfigGenerator; +use App\itnovum\openITCOCKPIT\ConfigGenerator\ContainerConfigInterface; use Cake\Core\Configure; -class PhpFpmOitc extends ConfigGenerator implements ConfigInterface { +class PhpFpmOitc extends ConfigGenerator implements ConfigInterface, ContainerConfigInterface { /** @var string */ protected $templateDir = 'PhpFpmOitc'; /** @var string */ @@ -86,6 +87,27 @@ public function getHelpText($key) { return ''; } + public function getValuesFromEnvironment() { + return [ + [ + 'key' => 'max_children', + 'value' => env('OITC_PHP_FPM_PM_MAX_CHILDREN', 5), + ], + [ + 'key' => 'start_servers', + 'value' => env('OITC_PHP_FPM_PM_START_SERVERS', 2), + ], + [ + 'key' => 'min_spare_servers', + 'value' => env('OITC_PHP_FPM_PM_MIN_SPARE_SERVERS', 1), + ], + [ + 'key' => 'max_spare_servers', + 'value' => env('OITC_PHP_FPM_PM_MAX_SPARE_SERVERS', 3), + ] + ]; + } + /** * @param array $dbRecords * @return bool|int diff --git a/src/itnovum/openITCOCKPIT/Core/MonitoringEngine/NagiosConfigGenerator.php b/src/itnovum/openITCOCKPIT/Core/MonitoringEngine/NagiosConfigGenerator.php index f358ef754b..7d23cbcfef 100644 --- a/src/itnovum/openITCOCKPIT/Core/MonitoringEngine/NagiosConfigGenerator.php +++ b/src/itnovum/openITCOCKPIT/Core/MonitoringEngine/NagiosConfigGenerator.php @@ -25,6 +25,7 @@ namespace itnovum\openITCOCKPIT\Core\MonitoringEngine; +use App\itnovum\openITCOCKPIT\Supervisor\Supervisorctl; use App\Lib\ExportTasks; use App\Lib\PluginExportTasks; use App\Model\Entity\CalendarHoliday; @@ -2718,7 +2719,17 @@ public function afterExportExternalTasks() { } //Restart oitc CMD to wipe old cached information - exec('systemctl restart oitc_cmd'); + if (IS_CONTAINER) { + try { + $Supervisorctl = new Supervisorctl(); + $Supervisorctl->restart('oitc_cmd'); + } catch (\Exception $e) { + Log::error('NagiosConfigGenerator: ' . $e->getMessage()); + } + } else { + exec('systemctl restart oitc_cmd'); + } + $ExportTasks = new ExportTasks(); foreach ($ExportTasks->getTasks() as $task) { diff --git a/src/itnovum/openITCOCKPIT/Core/NodeJS/ChartRenderClient.php b/src/itnovum/openITCOCKPIT/Core/NodeJS/ChartRenderClient.php index 5115c6975e..7dbed571e8 100644 --- a/src/itnovum/openITCOCKPIT/Core/NodeJS/ChartRenderClient.php +++ b/src/itnovum/openITCOCKPIT/Core/NodeJS/ChartRenderClient.php @@ -70,6 +70,11 @@ class ChartRenderClient { private $endTimestamp = 0; public function __construct() { + $address = env('OITC_PUPPETEER_ADDRESS', null); + if (!empty($address)) { + $this->address = $address; + } + $this->Client = new Client([ 'base_uri' => $this->address, 'proxy' => [ diff --git a/src/itnovum/openITCOCKPIT/Core/System/Health/MonitoringEngine.php b/src/itnovum/openITCOCKPIT/Core/System/Health/MonitoringEngine.php index bf0b426a1e..d249f5d0f2 100644 --- a/src/itnovum/openITCOCKPIT/Core/System/Health/MonitoringEngine.php +++ b/src/itnovum/openITCOCKPIT/Core/System/Health/MonitoringEngine.php @@ -25,7 +25,10 @@ namespace itnovum\openITCOCKPIT\Core\System\Health; +use App\itnovum\openITCOCKPIT\Supervisor\Binarydctl; +use App\itnovum\openITCOCKPIT\Supervisor\Supervisorctl; use Cake\Core\Configure; +use Cake\Log\Log; class MonitoringEngine { @@ -138,6 +141,11 @@ class MonitoringEngine { private $delimiter = '|'; public function __construct() { + if (IS_CONTAINER) { + // We always use Naemon for containers + $this->monitoringEngine = 'Naemon Core Container'; + return; + } Configure::load('nagios'); exec(Configure::read('nagios.basepath') . Configure::read('nagios.bin') . Configure::read('nagios.nagios_bin') . ' --version | head -n 2', $output); @@ -155,6 +163,11 @@ public function getMonitoringEngine() { * @return bool */ public function isNaemon() { + if (IS_CONTAINER) { + // We always use Naemon for containers + return true; + } + $monitoringEngine = strtolower($this->monitoringEngine); if (preg_match('/naemon/', $monitoringEngine)) { return true; @@ -187,16 +200,30 @@ public function getNagiostatsCommand() { /** * @return array|false */ - public function runNagiostats(){ - exec($this->getNagiostatsCommand(), $output); + public function runNagiostats() { + $output = []; + if (IS_CONTAINER) { + // Naemon is running inside a container - query remote supervisor to run "naemon -v naemon.cfg" + try { + $Binarydctl = new Binarydctl(); + $BinarydEndpoint = $Binarydctl->getBinarydApiEndpointByServiceName('naemon-stats'); + $result = $BinarydEndpoint->execute('naemon-stats'); + $output = [$result]; + } catch (\Exception $e) { + Log::error($e->getMessage()); + } + } else { + // Local running Naemon + exec($this->getNagiostatsCommand(), $output); + } - // Nagios and Naemon add the delimiter also the the end of the string - // this is bad, because explode will create and empty value in the array - // and this throw a warning in array_combine + // Nagios and Naemon add the delimiter also to the end of the string + // this is bad, because explode will create and empty value in the array + // and this throw a warning in array_combine $result = explode($this->delimiter, $output[0]); $result_sizeof = sizeof($result); if (sizeof($this->MRTG) < $result_sizeof) { - if ($result[$result_sizeof - 1] == '') { + if (trim($result[$result_sizeof - 1]) == '') { unset($result[$result_sizeof - 1]); } } diff --git a/src/itnovum/openITCOCKPIT/Core/System/Health/StatisticsCollector.php b/src/itnovum/openITCOCKPIT/Core/System/Health/StatisticsCollector.php index a9bf89f471..9e91b70014 100644 --- a/src/itnovum/openITCOCKPIT/Core/System/Health/StatisticsCollector.php +++ b/src/itnovum/openITCOCKPIT/Core/System/Health/StatisticsCollector.php @@ -100,7 +100,8 @@ public function getData() { 'version' => $LsbRelease->getVersion(), 'codename' => $LsbRelease->getCodename() ], - 'mysql' => $MysqlHealth->getAllMetrics() + 'mysql' => $MysqlHealth->getAllMetrics(), + 'containerized' => (IS_CONTAINER) ? true : false ]; } diff --git a/src/itnovum/openITCOCKPIT/Core/System/Health/SystemId.php b/src/itnovum/openITCOCKPIT/Core/System/Health/SystemId.php index 6f604468e8..f02a7e0f8d 100644 --- a/src/itnovum/openITCOCKPIT/Core/System/Health/SystemId.php +++ b/src/itnovum/openITCOCKPIT/Core/System/Health/SystemId.php @@ -29,15 +29,28 @@ class SystemId { + /** + * @var string|null + */ private $systemId = null; + /** + * @var string + */ + private $basePath = '/opt/openitc/etc'; + public function __construct() { - if (file_exists('/opt/openitc/etc/system-id')) { - $this->systemId = trim(file_get_contents('/opt/openitc/etc/system-id')); + if (IS_CONTAINER) { + // This is by default a docker volume so the ID will be persistent + $this->basePath = '/opt/openitc/var'; + } + + if (file_exists($this->basePath . DS . 'system-id')) { + $this->systemId = trim(file_get_contents($this->basePath . DS . 'system-id')); return; } else { - if (is_writable('/opt/openitc/etc')) { - $file = fopen('/opt/openitc/etc/system-id', 'w+'); + if (is_writable($this->basePath)) { + $file = fopen($this->basePath . DS . 'system-id', 'w+'); $this->systemId = UUID::v4(); fwrite($file, $this->systemId); fclose($file); diff --git a/src/itnovum/openITCOCKPIT/Grafana/GrafanaPanel.php b/src/itnovum/openITCOCKPIT/Grafana/GrafanaPanel.php index 94e9331a47..2f9280f918 100644 --- a/src/itnovum/openITCOCKPIT/Grafana/GrafanaPanel.php +++ b/src/itnovum/openITCOCKPIT/Grafana/GrafanaPanel.php @@ -55,11 +55,6 @@ class GrafanaPanel { */ private $ColorOverrides; - /** - * @var GrafanaYAxes - */ - private $YAxes; - /** * @var GrafanaThresholdCollection */ @@ -85,6 +80,12 @@ class GrafanaPanel { */ private $stacking_mode = 'none'; + /** + * @var string|null + * This unit is used for all metrics in the panel + */ + private $defaultUnit = null; + /** * @var array @@ -165,6 +166,7 @@ class GrafanaPanel { "color" => [ "mode" => "palette-classic" ], + "unit" => null, "thresholds" => [ "steps" => [] ] @@ -240,6 +242,8 @@ public function getPanelAsArray() { $this->panel['fieldConfig']['overrides'] = $this->ColorOverrides->getOverrides(); } + $this->panel['fieldConfig']['defaults']['unit'] = $this->defaultUnit; + return $this->panel; } @@ -267,20 +271,24 @@ public function setStackingMode($stackingMode) { /** * @param GrafanaTargetCollection $grafanaTargetCollection * @param GrafanaOverrides $Overrides - * @param GrafanaYAxes $YAxes * @param GrafanaThresholdCollection $ThresholdCollection */ public function addTargets( GrafanaTargetCollection $grafanaTargetCollection, GrafanaOverrides $Overrides, GrafanaColorOverrides $ColorOverrides, - GrafanaYAxes $YAxes, GrafanaThresholdCollection $ThresholdCollection ) { + + if ($grafanaTargetCollection->canDisplayUnits()) { + $units = $grafanaTargetCollection->getUnits(); + // Set the first unit as default unit for the panel + $this->defaultUnit = $units[0] ?? null; + } + $this->targets = $grafanaTargetCollection->getTargetsAsArray(); $this->Overrides = $Overrides; $this->ColorOverrides = $ColorOverrides; - $this->YAxes = $YAxes; $this->ThresholdCollection = $ThresholdCollection; } diff --git a/src/itnovum/openITCOCKPIT/Grafana/GrafanaYAxes.php b/src/itnovum/openITCOCKPIT/Grafana/GrafanaYAxes.php deleted file mode 100644 index 74aa8fbab9..0000000000 --- a/src/itnovum/openITCOCKPIT/Grafana/GrafanaYAxes.php +++ /dev/null @@ -1,87 +0,0 @@ - -// -// This file is dual licensed -// -// 1. -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 3 of the License. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -// 2. -// If you purchased an openITCOCKPIT Enterprise Edition you can use this file -// under the terms of the openITCOCKPIT Enterprise Edition license agreement. -// License agreement and license key will be shipped with the order -// confirmation. - - -namespace itnovum\openITCOCKPIT\Grafana; - - -class GrafanaYAxes { - - /** - * @var array - */ - private $axes = []; - - /** - * @var array - */ - private $defaultAxe = [ - "format" => "short", - "label" => null, - "logBase" => 1, - "max" => null, - "min" => null, - "show" => true - ]; - - /** - * GrafanaYAxes constructor. - * @param GrafanaTargetCollection $targetCollection - */ - public function __construct(GrafanaTargetCollection $targetCollection) { - if ($targetCollection->canDisplayUnits()) { - foreach ($targetCollection->getUnits() as $unit) { - - $max = null; - $min = null; - if ($unit === 'percent') { - $max = 100; - $min = 0; - } - - $this->axes[] = [ - "format" => $unit, - "label" => null, - "logBase" => 1, - "max" => $max, - "min" => $min, - "show" => true - ]; - } - } - } - - /** - * @return mixed - */ - public function getAxesAsArray() { - $axes = $this->axes; - while (sizeof($axes) < 2) { - $axes[] = $this->defaultAxe; - } - return $axes; - } - -} diff --git a/src/itnovum/openITCOCKPIT/Monitoring/QueryHandler.php b/src/itnovum/openITCOCKPIT/Monitoring/QueryHandler.php index 8cb2e6546d..97936e25b6 100644 --- a/src/itnovum/openITCOCKPIT/Monitoring/QueryHandler.php +++ b/src/itnovum/openITCOCKPIT/Monitoring/QueryHandler.php @@ -106,6 +106,14 @@ public function getPath() { return $this->queryHandler; } + /** + * When openITCOCKPIT is running in a Docker container, it HAS NO ACCESS to the query handler socket + * @return bool + */ + public function isContainer(){ + return IS_CONTAINER; + } + /** * @return string */ @@ -190,4 +198,4 @@ private function terminate() { $this->query .= "\0"; } -} \ No newline at end of file +} diff --git a/src/itnovum/openITCOCKPIT/Supervisor/ApiException.php b/src/itnovum/openITCOCKPIT/Supervisor/ApiException.php new file mode 100644 index 0000000000..26b254ec52 --- /dev/null +++ b/src/itnovum/openITCOCKPIT/Supervisor/ApiException.php @@ -0,0 +1,7 @@ + +// +// Licensed under The MIT License +// + + +namespace App\itnovum\openITCOCKPIT\Supervisor; + +use GuzzleHttp\Client; +use GuzzleHttp\Exception\ClientException; +use GuzzleHttp\Exception\ConnectException; +use GuzzleHttp\RequestOptions; +use itnovum\openITCOCKPIT\Core\NodeJS\ErrorPdf; + +class BinarydAPI { + + /** + * @var Client + */ + private $Client; + + public function __construct(string $address) { + $this->Client = new Client([ + 'base_uri' => $address, + 'proxy' => [ + 'http' => false, + 'https' => false + ] + ]); + } + + /** + * Executes the command and returns the RAW output + * + * @param string $programName + * @return string + * @throws \GuzzleHttp\Exception\GuzzleException + */ + public function execute(string $programName) { + $response = $this->Client->get('/' . $programName); + return $response->getBody()->getContents(); + } + + + /** + * Executes the command via the /json/ API of binaryd + * This returns the output and return code + * + * @param string $programName + * @return array + * @throws \GuzzleHttp\Exception\GuzzleException + */ + public function executeJson(string $programName) { + $response = $this->Client->get('/json/' . $programName); + $result = $response->getBody()->getContents(); + return json_decode($result, true); + } +} diff --git a/src/itnovum/openITCOCKPIT/Supervisor/Binarydctl.php b/src/itnovum/openITCOCKPIT/Supervisor/Binarydctl.php new file mode 100644 index 0000000000..9632f581fd --- /dev/null +++ b/src/itnovum/openITCOCKPIT/Supervisor/Binarydctl.php @@ -0,0 +1,42 @@ +getSupervisorApiEndpointByServiceName($serviceName); + return $SupervisorApi->startProcess($serviceName); + } + + /** + * @param string $serviceName + * @return bool|string + * @throws ApiException + */ + public function stop(string $serviceName) { + $SupervisorApi = $this->getSupervisorApiEndpointByServiceName($serviceName); + return $SupervisorApi->stopProcess($serviceName); + } + + /** + * @param string $serviceName + * @return bool|string + * @throws ApiException + */ + public function restart(string $serviceName) { + $SupervisorApi = $this->getSupervisorApiEndpointByServiceName($serviceName); + $SupervisorApi->stopProcess($serviceName); + return $SupervisorApi->startProcess($serviceName); + } + + /** + * @param string $serviceName + * @return array|string + * @throws ApiException + */ + public function status(string $serviceName) { + $SupervisorApi = $this->getSupervisorApiEndpointByServiceName($serviceName); + return $SupervisorApi->getProcessInfo($serviceName); + } + + /** + * @param string $serviceName + * @return XMLRPCApi + * @throws \RuntimeException + */ + public function getSupervisorApiEndpointByServiceName(string $serviceName): XMLRPCApi { + // If you edit this like, also make sure to edit + // SupervisorCommand::buildOptionParser() + + $username = env('SUPERVISOR_USER', 'supervisord'); + $password = env('SUPERVISOR_PASSWORD', 'password'); + + switch ($serviceName) { + case 'naemon': + // Tell the Naemon Container to start|stop|restart Naemon + $url = sprintf( + 'http://%s:%s/RPC2', + env('OITC_NAEMON_HOSTNAME', 'naemon'), + env('SUPERVISOR_PORT', 9001) + ); + $SupervisorApi = new XMLRPCApi($username, $password, $url); + break; + + case 'statusengine': + // Tell the Naemon Container to start|stop|restart Naemon + $url = sprintf( + 'http://%s:%s/RPC2', + env('OITC_STATUSENGINE_WORKER_HOSTNAME', 'statusengine-worker'), + env('SUPERVISOR_PORT', 9001) + ); + $SupervisorApi = new XMLRPCApi($username, $password, $url); + break; + + default: + // Service is running in the same container as openITCOCKPIT itslef + $SupervisorApi = new XMLRPCApi($username, $password, 'http://127.0.0.1:9001/RPC2'); + break; + } + + if (!isset($SupervisorApi) || !($SupervisorApi instanceof XMLRPCApi)) { + throw new \RuntimeException('No SupervisorAPI endpoint found for given service_name'); + } + + return $SupervisorApi; + } + + public function isRunning(string $serviceName): bool { + try { + $SupervisorApi = $this->getSupervisorApiEndpointByServiceName($serviceName); + $result = $SupervisorApi->getProcessInfo($serviceName); + if (isset($result['statename'])) { + if($result['statename'] === 'STARTING' || $result['statename'] === 'RUNNING') { + // We consider STARTING as running is this case as the process itself is started + return true; + } + } + } catch (\Exception $e) { + Log::error($e->getMessage()); + } + + return false; + } + +} + diff --git a/src/itnovum/openITCOCKPIT/Supervisor/XMLRPCApi.php b/src/itnovum/openITCOCKPIT/Supervisor/XMLRPCApi.php new file mode 100644 index 0000000000..8314dd9a60 --- /dev/null +++ b/src/itnovum/openITCOCKPIT/Supervisor/XMLRPCApi.php @@ -0,0 +1,576 @@ + +// +// This file is dual licensed +// +// 1. +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// 2. +// If you purchased an openITCOCKPIT Enterprise Edition you can use this file +// under the terms of the openITCOCKPIT Enterprise Edition license agreement. +// License agreement and license key will be shipped with the order +// confirmation. + +namespace App\itnovum\openITCOCKPIT\Supervisor; + +/** + * Helper class for easy communication with the Supervisor XML-RPC API + * Please notice: This class is a Wrapper for the API. Not all API methods are implemented. + * + * If you are missing methods, please see the docs and implement the missing method: + * http://supervisord.org/api.htm + * + * This class does not make use of any __call() Magic Methods. This makes sure that the IDE knows all + * methods and can provide useful autocomplete and knows about method signatures + */ +class XMLRPCApi { + + /** + * @var string + */ + private $username; + + /** + * @var string + */ + private $password; + + /** + * @var string + */ + private $url; + + /** + * Connection timeout in seconds + * @var + */ + private $timeout; + + /** + * @param string $username + * @param string $password + * @param string $url Full API url with protocol and port like http://127.0.0.1:9001/RPC2 + */ + public function __construct(string $username, string $password, string $url = 'http://127.0.0.1:9001/RPC2', $timeout = 60) { + $this->username = $username; + $this->password = $password; + $this->url = $url; + $this->timeout = $timeout; + } + + /** + * Send request to the XML-PRC API of Supervisor + * + * @param string $functionName API Method name you want to call + * @param mixed $functionArgs Args you want to pass to the API + * @return mixed|string + * @throws ApiException + */ + public function request(string $functionName, $functionArgs = null) { + $request = xmlrpc_encode_request($functionName, $functionArgs); + $header = []; + $header[] = 'Authorization: Basic ' . base64_encode($this->username . ':' . $this->password); + $header[] = "Content-type: text/xml"; + $header[] = "Content-length: " . strlen($request); + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $this->url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout); + curl_setopt($ch, CURLOPT_HTTPHEADER, $header); + curl_setopt($ch, CURLOPT_POSTFIELDS, $request); + curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, false); + curl_setopt($ch, CURLOPT_PROXY, ''); + + $data = curl_exec($ch); + + $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); + if ($status !== 200) { + throw new ApiException(sprintf('HTTP status code was: %s', $status)); + } + + if (curl_errno($ch)) { + throw new ApiException(curl_error($ch)); + } + + curl_close($ch); + return xmlrpc_decode($data); + } + + /** + * Return the version of the RPC API used by supervisord + * + * @see http://supervisord.org/api.html#supervisor.rpcinterface.SupervisorNamespaceRPCInterface.getAPIVersion + * @return string version id + * @throws ApiException + */ + public function getApiVersion() { + return $this->request('supervisor.getAPIVersion'); + } + + /** + * Return the version of the supervisor package in use by supervisord + * + * @see http://supervisord.org/api.html#supervisor.rpcinterface.SupervisorNamespaceRPCInterface.getSupervisorVersion + * @return string version id + * @throws ApiException + */ + public function getSupervisorVersion() { + return $this->request('supervisor.getSupervisorVersion'); + } + + /** + * Return identifying string of supervisord + * + * @see http://supervisord.org/api.html#supervisor.rpcinterface.SupervisorNamespaceRPCInterface.getIdentification + * @return string identifier identifying string + * @throws ApiException + */ + public function getIdentification() { + return $this->request('supervisor.getIdentification'); + } + + /** + * Return current state of supervisord as a struct + * + * | statecode | statename | Description | + * |-----------|------------|------------------------------------------------| + * | 2 | FATAL | Supervisor has experienced a serious error. | + * | 1 | RUNNING | Supervisor is working normally. | + * | 0 | RESTARTING | Supervisor is in the process of restarting. | + * | -1 | SHUTDOWN | Supervisor is in the process of shutting down. | + * + * Return + * [ + * 'statecode' => (int) 1, + * 'statename' => 'RUNNING' + * ] + * + * @see http://supervisord.org/api.html#supervisor.rpcinterface.SupervisorNamespaceRPCInterface.getState + * @return array A struct with keys int statecode, string statename + * @throws ApiException + */ + public function getState() { + return $this->request('supervisor.getState'); + } + + /** + * Return the PID of supervisord + * + * @see http://supervisord.org/api.html#supervisor.rpcinterface.SupervisorNamespaceRPCInterface.getPID + * @return int PID + * @throws ApiException + */ + public function getPID() { + return $this->request('supervisor.getPID'); + } + + /** + * Read length bytes from the main log starting at offset + * + * | Offset | Length | Behavior of readProcessLog | + * |------------------|----------|--------------------------------------------------------------------------------| + * | Negative | Not Zero | Bad arguments. This will raise the fault BAD_ARGUMENTS. | + * | Negative | Zero | This will return the tail of the log, or offset number of characters from the | + * | | | end of the log. For example, if offset = -4 and length = 0, then the last four | + * | | |characters will be returned from the end of the log. | + * | Zero or Positive | Negative | Bad arguments. This will raise the fault BAD_ARGUMENTS. | + * | Zero or Positive | Zero | All characters will be returned from the offset specified. | + * | Zero or Positive | Positive | A number of characters length will be returned from the offset. | + * + * + * @see http://supervisord.org/api.html#supervisor.rpcinterface.SupervisorNamespaceRPCInterface.readLog + * + * @param int $offset offset to start reading from. + * @param int $length number of bytes to read from the log + * @return string result Bytes of log + * @throws ApiException + */ + public function readLog(int $offset = 0, int $length = 1024) { + return $this->request('supervisor.readLog', [$offset, $length]); + } + + /** + * Clear the main log + * + * @see http://supervisord.org/api.html#supervisor.rpcinterface.SupervisorNamespaceRPCInterface.clearLog + * @return bool result always returns True unless error + * @throws ApiException + */ + public function clearLog() { + return $this->request('supervisor.clearLog'); + } + + /** + * Shut down the supervisor process + * + * @see http://supervisord.org/api.html#supervisor.rpcinterface.SupervisorNamespaceRPCInterface.shutdown + * @return bool result always returns True unless error + * @throws ApiException + */ + public function shutdown() { + return $this->request('supervisor.shutdown'); + } + + /** + * Restart the supervisor process + * + * @see http://supervisord.org/api.html#supervisor.rpcinterface.SupervisorNamespaceRPCInterface.restart + * @return bool result always return True unless error + * @throws ApiException + */ + public function restart() { + return $this->request('supervisor.restart'); + } + + /** + * Return an array listing the available method names + * + * @see http://supervisord.org/api.html#supervisor.xmlrpc.SystemNamespaceRPCInterface.listMethods + * @return array result An array of method names available (strings). + * @throws ApiException + */ + public function listMethods() { + return $this->request('system.listMethods'); + } + + /** + * Return a string showing the method’s documentation + * + * @see http://supervisord.org/api.html#supervisor.xmlrpc.SystemNamespaceRPCInterface.methodHelp + * @param string $method The name of the method + * @return string result The documentation for the method name. + * @throws ApiException + */ + public function methodHelp(string $method) { + return $this->request('system.methodHelp', [$method]); + } + + /** + * Return an array describing the method signature in the form [rtype, ptype, ptype…] where rtype is + * the return data type of the method, and ptypes are the parameter data types that the method accepts + * in method argument order. + * + * @see http://supervisord.org/api.html#supervisor.xmlrpc.SystemNamespaceRPCInterface.methodSignature + * @param string $method The name of the method + * @return array result The result. + * @throws ApiException + */ + public function methodSignature(string $method) { + return $this->request('system.methodSignature', [$method]); + } + + /** + * Get info about a process named name + * + * Returns + * [ + * 'name' => 'sudo_server', + * 'group' => 'sudo_server', + * 'start' => (int) 1683792729, + * 'stop' => (int) 0, + * 'now' => (int) 1683792818, + * 'state' => (int) 200, + * 'statename' => 'FATAL', + * 'spawnerr' => 'unknown error making dispatchers for 'sudo_server': ENXIO', + * 'exitstatus' => (int) 0, + * 'logfile' => '/dev/stdout', + * 'stdout_logfile' => '/dev/stdout', + * 'stderr_logfile' => '', + * 'pid' => (int) 0, + * 'description' => 'unknown error making dispatchers for 'sudo_server': ENXIO' + * ] + * + * Process States + * | Statename | Code | Description | + * |-----------|------|-----------------------------------------------------------------------------------------| + * | STOPPED | 0 | The process has been stopped due to a stop request or has never been started. | + * | STARTING | 10 | The process is starting due to a start request. | + * | RUNNING | 20 | The process is running. | + * | BACKOFF | 30 | The process entered the STARTING state but subsequently exited too quickly | + * | | | (before the time defined in startsecs) to move to the RUNNING state. | + * | STOPPING | 40 | The process is stopping due to a stop request. | + * | EXITED | 100 | The process exited from the RUNNING state (expectedly or unexpectedly). | + * | FATAL | 200 | The process could not be started successfully. | + * | UNKNOWN | 1000 | The process is in an unknown state (supervisord programming error). | + * + * @see http://supervisord.org/api.html#supervisor.rpcinterface.SupervisorNamespaceRPCInterface.getProcessInfo + * @param string $name The name of the process (or ‘group:name’) + * @return array result A structure containing data about the process + * @throws ApiException + */ + public function getProcessInfo(string $name) { + return $this->request('supervisor.getProcessInfo', [$name]); + } + + /** + * Get info about all processes + * + * @see http://supervisord.org/api.html#supervisor.rpcinterface.SupervisorNamespaceRPCInterface.getAllProcessInfo + * @return array result An array of process status results + * @throws ApiException + */ + public function getAllProcessInfo() { + return $this->request('supervisor.getAllProcessInfo'); + } + + /** + * Start a process + * + * @see http://supervisord.org/api.html#supervisor.rpcinterface.SupervisorNamespaceRPCInterface.startProcess + * @param string $name Process name (or group:name, or group:*) + * @param boolean $wait Wait for process to be fully started + * @return boolean result Always true unless error + * @throws ApiException + */ + public function startProcess(string $name, bool $wait = true) { + return $this->request('supervisor.startProcess', [$name, $wait]); + } + + /** + * Start all processes listed in the configuration file + * + * @see http://supervisord.org/api.html#supervisor.rpcinterface.SupervisorNamespaceRPCInterface.startAllProcesses + * @param boolean $wait Wait for each process to be fully started + * @return array result An array of process status info structs + * @throws ApiException + */ + public function startAllProcesses($wait = true) { + return $this->request('supervisor.startAllProcesses', [$wait]); + } + + /** + * Start all processes in the group named ‘name’ + * + * @see http://supervisord.org/api.html#supervisor.rpcinterface.SupervisorNamespaceRPCInterface.startProcessGroup + * @param string $name The group name + * @param boolean $wait Wait for each process to be fully started + * @return array result An array of process status info structs + * @throws ApiException + */ + public function startProcessGroup(string $name, $wait = true) { + return $this->request('supervisor.startProcessGroup', [$name, $wait]); + } + + /** + * Stop a process named by name + * + * @see http://supervisord.org/api.html#supervisor.rpcinterface.SupervisorNamespaceRPCInterface.stopProcess + * @param string $name The name of the process to stop (or ‘group:name’) + * @param boolean $wait Wait for the process to be fully stopped + * @return boolean result Always return True unless error + * @throws ApiException + */ + public function stopProcess(string $name, $wait = true) { + return $this->request('supervisor.stopProcess', [$name, $wait]); + } + + /** + * Stop all processes in the process list + * + * @see http://supervisord.org/api.html#supervisor.rpcinterface.SupervisorNamespaceRPCInterface.stopAllProcesses + * @param boolean $wait Wait for each process to be fully stopped + * @return array result An array of process status info structs + * @throws ApiException + */ + public function stopAllProcesses($wait = true) { + return $this->request('supervisor.stopAllProcesses', [$wait]); + } + + /** + * Stop all processes in the process group named ‘name’ + * + * @see http://supervisord.org/api.html#supervisor.rpcinterface.SupervisorNamespaceRPCInterface.stopProcessGroup + * @param string $name The group name + * @param boolean $wait Wait for each process to be fully stopped + * @return array result An array of process status info structs + * @throws ApiException + */ + public function stopProcessGroup(string $name, $wait = true) { + return $this->request('supervisor.stopProcessGroup', [$name, $wait]); + } + + /** + * Send an arbitrary UNIX signal to the process named by name + * + * @see http://supervisord.org/api.html#supervisor.rpcinterface.SupervisorNamespaceRPCInterface.signalProcess + * @param string $name Name of the process to signal (or ‘group:name’) + * @param string|int $signal Signal to send, as name (‘HUP’) or number (‘1’) + * @return boolean + * @throws ApiException + */ + public function signalProcess(string $name, $signal) { + return $this->request('supervisor.signalProcess', [$name, $signal]); + } + + /** + * Send a signal to all processes in the process list + * + * @see http://supervisord.org/api.html#supervisor.rpcinterface.SupervisorNamespaceRPCInterface.signalAllProcesses + * @param string|int $signal Signal to send, as name (‘HUP’) or number (‘1’) + * @return array An array of process status info structs + * @throws ApiException + */ + public function signalAllProcesses($signal) { + return $this->request('supervisor.signalAllProcesses', [$signal]); + } + + /** + * Send a signal to all processes in the group named ‘name’ + * + * @see http://supervisord.org/api.html#supervisor.rpcinterface.SupervisorNamespaceRPCInterface.signalProcessGroup + * @param string $name The group name + * @param string|int $signal Signal to send, as name (‘HUP’) or number (‘1’) + * @return array + * @throws ApiException + */ + public function signalProcessGroup(string $name, $signal) { + return $this->request('supervisor.signalProcessGroup', [$name, $signal]); + } + + /** + * Send a string of chars to the stdin of the process name. If non-7-bit data is sent (unicode), + * it is encoded to utf-8 before being sent to the process’ stdin. If chars is not a string or + * is not unicode, raise INCORRECT_PARAMETERS. If the process is not running, raise NOT_RUNNING. + * If the process’ stdin cannot accept input (e.g. it was closed by the child process), raise NO_FILE. + * + * @see http://supervisord.org/api.html#supervisor.rpcinterface.SupervisorNamespaceRPCInterface.sendProcessStdin + * @param string $name The process name to send to (or ‘group:name’) + * @param string $chars The character data to send to the process + * @return boolean result Always return True unless error + * @throws ApiException + */ + public function sendProcessStdin(string $name, string $chars) { + return $this->request('supervisor.sendProcessStdin', [$name, $chars]); + } + + /** + * Send an event that will be received by event listener subprocesses subscribing to the RemoteCommunicationEvent. + * + * @see http://supervisord.org/api.html#supervisor.rpcinterface.SupervisorNamespaceRPCInterface.sendRemoteCommEvent + * @param string $type String for the “type” key in the event header + * @param string $data Data for the event body + * @return boolean Always return True unless error + * @throws ApiException + */ + public function sendRemoteCommEvent(string $type, string $data) { + return $this->request('supervisor.sendRemoteCommEvent', [$type, $data]); + } + + /** + * Reload the configuration. + * The result contains three arrays containing names of process groups: + * - added gives the process groups that have been added + * - changed gives the process groups whose contents have changed + * - removed gives the process groups that are no longer in the configuration + * + * @see http://supervisord.org/api.html#supervisor.rpcinterface.SupervisorNamespaceRPCInterface.reloadConfig + * @return array result [[added, changed, removed]] + * @throws ApiException + */ + public function reloadConfig() { + return $this->request('supervisor.reloadConfig'); + } + + /** + * Read length bytes from name’s stdout log starting at offset + * + * @see http://supervisord.org/api.html#supervisor.rpcinterface.SupervisorNamespaceRPCInterface.readProcessStdoutLog + * @param string $name the name of the process (or ‘group:name’) + * @param int $offset offset to start reading from + * @param int $length number of bytes to read from the log + * @return string result Bytes of log + * @throws ApiException + */ + public function readProcessStdoutLog(string $name, int $offset, int $length = 1024) { + return $this->request('supervisor.readProcessStdoutLog', [$name, $offset, $length]); + } + + /** + * Read length bytes from name’s stdout log starting at offset + * + * @see http://supervisord.org/api.html#supervisor.rpcinterface.SupervisorNamespaceRPCInterface.readProcessStderrLog + * @param string $name the name of the process (or ‘group:name’) + * @param int $offset offset to start reading from + * @param int $length number of bytes to read from the log + * @return string result Bytes of log + * @throws ApiException + */ + public function readProcessStderrLog(string $name, int $offset, int $length = 1024) { + return $this->request('supervisor.readProcessStderrLog', [$name, $offset, $length]); + } + + /** + * Provides a more efficient way to tail the (stdout) log than readProcessStdoutLog(). Use readProcessStdoutLog() + * to read chunks and tailProcessStdoutLog() to tail. + * + * Requests (length) bytes from the (name)’s log, starting at (offset). If the total log size is greater + * than (offset + length), the overflow flag is set and the (offset) is automatically increased to position + * the buffer at the end of the log. If less than (length) bytes are available, the maximum number of available + * bytes will be returned. (offset) returned is always the last offset in the log +1. + * + * @see http://supervisord.org/api.html#supervisor.rpcinterface.SupervisorNamespaceRPCInterface.tailProcessStdoutLog + * @param string $name the name of the process (or ‘group:name’) + * @param int $offset offset to start reading from + * @param int $length number of bytes to read from the log + * @return array result [string bytes, int offset, bool overflow] + * @throws ApiException + */ + public function tailProcessStdoutLog(string $name, int $offset, int $length = 1024) { + return $this->request('supervisor.tailProcessStdoutLog', [$name, $offset, $length]); + } + + /** + * Provides a more efficient way to tail the (stderr) log than readProcessStderrLog(). Use readProcessStderrLog() + * to read chunks and tailProcessStderrLog() to tail. + * + * Requests (length) bytes from the (name)’s log, starting at (offset). If the total log size is greater + * than (offset + length), the overflow flag is set and the (offset) is automatically increased to position + * the buffer at the end of the log. If less than (length) bytes are available, the maximum number of available + * bytes will be returned. (offset) returned is always the last offset in the log +1. + * + * @see http://supervisord.org/api.html#supervisor.rpcinterface.SupervisorNamespaceRPCInterface.tailProcessStderrLog + * @param string $name the name of the process (or ‘group:name’) + * @param int $offset offset to start reading from + * @param int $length number of bytes to read from the log + * @return array result [string bytes, int offset, bool overflow] + * @throws ApiException + */ + public function tailProcessStderrLog(string $name, int $offset, int $length = 1024) { + return $this->request('supervisor.tailProcessStderrLog', [$name, $offset, $length]); + } + + /** + * Clear the stdout and stderr logs for the named process and reopen them. + * + * @see http://supervisord.org/api.html#supervisor.rpcinterface.SupervisorNamespaceRPCInterface.clearProcessLogs + * @param string $name The name of the process (or ‘group:name’) + * @return boolean result Always True unless error + * @throws ApiException + */ + public function clearProcessLogs(string $name) { + return $this->request('supervisor.clearProcessLogs', [$name]); + } + + /** + * Clear all process log files + * + * @see http://supervisord.org/api.html#supervisor.rpcinterface.SupervisorNamespaceRPCInterface.clearProcessLogs + * @return array result An array of process status info structs + * @throws ApiException + */ + public function clearAllProcessLogs() { + return $this->request('supervisor.clearAllProcessLogs'); + } +} + diff --git a/system/usr/bin/oitc b/system/usr/bin/oitc index 779b7af2c3..ea1bc09d39 100644 --- a/system/usr/bin/oitc +++ b/system/usr/bin/oitc @@ -1,10 +1,10 @@ #!/bin/bash - -preserveEnvSupportsList=$(sudo --help |grep "preserve-env" | wc -l) -if [[ $preserveEnvSupportsList -gt 1 ]]; then - sudo -g www-data --preserve-env=OITC_DEBUG /opt/openitc/frontend/bin/cake "$@" -else +# ITC-2986 keep all environment variables for config generation inside of docker containers +##preserveEnvSupportsList=$(sudo --help |grep "preserve-env" | wc -l) +##if [[ $preserveEnvSupportsList -gt 1 ]]; then +## sudo -g www-data --preserve-env=OITC_DEBUG /opt/openitc/frontend/bin/cake "$@" +##else # Old sudo version that does not support list of environment variables to preserve # Added with 9bb78048656f 2017-08-03 sudo -g www-data --preserve-env /opt/openitc/frontend/bin/cake "$@" -fi +##fi diff --git a/tests/TestCase/Command/SupervisorCommandTest.php b/tests/TestCase/Command/SupervisorCommandTest.php new file mode 100644 index 0000000000..638c72b960 --- /dev/null +++ b/tests/TestCase/Command/SupervisorCommandTest.php @@ -0,0 +1,50 @@ +useCommandRunner(); + } + /** + * Test buildOptionParser method + * + * @return void + * @uses \App\Command\SupervisorCommand::buildOptionParser() + */ + public function testBuildOptionParser(): void + { + $this->markTestIncomplete('Not implemented yet.'); + } + + /** + * Test execute method + * + * @return void + * @uses \App\Command\SupervisorCommand::execute() + */ + public function testExecute(): void + { + $this->markTestIncomplete('Not implemented yet.'); + } +}