#!/bin/sh set -e VAR_DIR=/var/opt/microsoft/omsagent ETC_DIR=/etc/opt/microsoft/omsagent NPM_DIR=$VAR_DIR/npm_state NPM_CONF_FILE_SUFFIX=conf/omsagent.d/npmd.conf DF_TMP_DIR=$VAR_DIR/tmp DF_RUN_DIR=$VAR_DIR/run DF_STATE_DIR=$VAR_DIR/state DF_LOG_DIR=$VAR_DIR/log DF_CERT_DIR=$ETC_DIR/certs DF_CONF_DIR=$ETC_DIR/conf TMP_DIR=$DF_TMP_DIR CERT_DIR=$DF_CERT_DIR CONF_DIR=$DF_CONF_DIR SYSCONF_DIR=$ETC_DIR/sysconf # Optional file with initial onboarding credentials FILE_ONBOARD=/etc/omsagent-onboard.conf # Generated conf file containing information for this script CONF_OMSADMIN=$CONF_DIR/omsadmin.conf # Omsagent daemon configuration CONF_OMSAGENT=$CONF_DIR/omsagent.conf # Omsagent proxy configuration CONF_PROXY=$ETC_DIR/proxy.conf PRE_MH_CONF_PROXY=$DF_CONF_DIR/proxy.conf # File with OS information for telemetry OS_INFO=/etc/opt/microsoft/scx/conf/scx-release # Service Control script SERVICE_CONTROL=/opt/microsoft/omsagent/bin/service_control # File with information about the agent installed INSTALL_INFO=/etc/opt/microsoft/omsagent/sysconf/installinfo.txt # Ruby helpers RUBY=/opt/microsoft/omsagent/ruby/bin/ruby AUTH_KEY_SCRIPT=/opt/microsoft/omsagent/bin/auth_key.rb TOPOLOGY_REQ_SCRIPT=/opt/microsoft/omsagent/plugin/agent_topology_request_script.rb MAINTENANCE_TASKS_SCRIPT=/opt/microsoft/omsagent/plugin/agent_maintenance_script.rb # Ruby error codes AGENT_MAINTENANCE_MISSING_CONFIG_FILE=4 AGENT_MAINTENANCE_MISSING_CONFIG=5 AGENT_MAINTENANCE_ERROR_WRITING_TO_FILE=12 # DSC MetaConfig generation script. User python3 over python2 if both versions are installed if [ -x "$(command -v python3)" ]; then METACONFIG_PY=/opt/microsoft/omsconfig/Scripts/python3/OMS_MetaConfigHelper.py elif [ -x "$(command -v python2)" ]; then METACONFIG_PY=/opt/microsoft/omsconfig/Scripts/OMS_MetaConfigHelper.py else # Failure to find python/the correct script path will only break onboarding (just one # of omsadmin's numerous functions); exit with failure in the onboard() function instead METACONFIG_PY="" fi # Certs FILE_KEY=$CERT_DIR/oms.key FILE_CRT=$CERT_DIR/oms.crt # Temporary files SHARED_KEY_FILE=$TMP_DIR/shared_key BODY_ONBOARD=$TMP_DIR/body_onboard.xml RESP_ONBOARD=$TMP_DIR/resp_onboard.xml ENDPOINT_FILE=$TMP_DIR/endpoints AGENT_USER=omsagent AGENT_GROUP=omiusers # Default settings URL_TLD=opinsights.azure.com WORKSPACE_ID="" AGENT_GUID="" LOG_FACILITY=local0 CERTIFICATE_UPDATE_ENDPOINT="" VERBOSE=0 AZURE_RESOURCE_ID="" OMSCLOUD_ID="" UUID="" USER_ID=`id -u` MULTI_HOMING_MARKER= DEFAULT_SYSLOG_PORT=25224 DEFAULT_MONITOR_AGENT_PORT=25324 SYSLOG_PORT=$DEFAULT_SYSLOG_PORT MONITOR_AGENT_PORT=$DEFAULT_MONITOR_AGENT_PORT # SCOM variables SCX_SSL_CONFIG=/opt/microsoft/scx/bin/tools/scxsslconfig OMI_CONF_FILE=/etc/opt/omi/conf/omiserver.conf OMI_CONF_EDITOR=/opt/omi/bin/omiconfigeditor # Space seperated list of non oms workspaces NON_OMS_WS="scom LAD" # Error codes and categories: # User configuration/parameters: INVALID_OPTION_PROVIDED=2 INVALID_CONFIG_PROVIDED=3 INVALID_PROXY=4 # Service-related: ERROR_ONBOARDING_403=5 ERROR_ONBOARDING_NON_200_HTTP=6 # Network-related: ERROR_RESOLVING_HOST=7 ERROR_ONBOARDING=8 # Internal errors: INTERNAL_ERROR=30 RUBY_ERROR_GENERATING_GUID=31 ERROR_GENERATING_CERTS=32 ERROR_GENERATING_METACONFIG=33 ERROR_METACONFIG_PY_NOT_PRESENT=34 # Dependency-related: INSTALL_PYTHON=60 # curl error codes: CURL_PROXY_RESOLVE_ERROR=5 CURL_HOST_RESOLVE_ERROR=6 CURL_CONNECT_HOST_ERROR=7 usage() { local basename=`basename $0` echo echo "Maintenance tool for OMS:" echo "Onboarding:" echo "$basename -w -s [-d ]" echo echo "List Workspaces:" echo "$basename -l" echo echo "Remove Workspace:" echo "$basename -x " echo echo "Remove All Workspaces:" echo "$basename -X" echo echo "Update workspace configuration and folder structure to multi-homing schema:" echo "$basename -U" echo echo "Onboard the workspace with a multi-homing marker. The workspace will be regarded as secondary." echo "$basename -m " echo echo "Define proxy settings ('-u' will prompt for password):" echo "$basename [-u user] -p host[:port]" echo echo "Azure resource ID:" echo "$basename -a " echo echo "Detect if omiserver is listening to SCOM port:" echo "$basename -o" } set_user_agent() { USER_AGENT=LinuxMonitoringAgent/`head -1 $INSTALL_INFO | awk '{print $1}'` } check_user() { if [ "$USER_ID" -ne "0" -a `id -un` != "$AGENT_USER" ]; then log_error "This script must be run as root or as the $AGENT_USER user." exit 1 fi } chown_omsagent() { # When this script is run as root, we still have to make sure the generated # files are owned by omsagent for everything to work properly [ "$USER_ID" -eq "0" ] && chown $AGENT_USER:$AGENT_GROUP $@ > /dev/null 2>&1 return 0 } save_config() { #Save configuration echo WORKSPACE_ID=$WORKSPACE_ID > $CONF_OMSADMIN echo AGENT_GUID=$AGENT_GUID >> $CONF_OMSADMIN echo LOG_FACILITY=$LOG_FACILITY >> $CONF_OMSADMIN echo CERTIFICATE_UPDATE_ENDPOINT=$CERTIFICATE_UPDATE_ENDPOINT >> $CONF_OMSADMIN echo URL_TLD=$URL_TLD >> $CONF_OMSADMIN echo DSC_ENDPOINT=$DSC_ENDPOINT >> $CONF_OMSADMIN echo OMS_ENDPOINT=https://$WORKSPACE_ID.ods.$URL_TLD/OperationalData.svc/PostJsonDataItems >> $CONF_OMSADMIN echo AZURE_RESOURCE_ID=$AZURE_RESOURCE_ID >> $CONF_OMSADMIN echo OMSCLOUD_ID=$OMSCLOUD_ID | tr -d ' ' >> $CONF_OMSADMIN echo UUID=$UUID | tr -d ' ' >> $CONF_OMSADMIN chown_omsagent "$CONF_OMSADMIN" } update_azure_resource_id() { sed -i "s,\(AZURE_RESOURCE_ID=\)\(.*\),\1$AZURE_RESOURCE_ID," $CONF_OMSADMIN echo "Azure Resource ID updated." } cleanup() { rm "$BODY_ONBOARD" "$RESP_ONBOARD" "$ENDPOINT_FILE" > /dev/null 2>&1 || true } # This helper should only be called in the cases when certs have been created but onboarding failed. cleanup_certs() { rm "$FILE_CRT" "$FILE_KEY" > /dev/null 2>&1 || true } clean_exit() { cleanup exit $1 } log_info() { echo -e "info\t$1" logger -i -p "$LOG_FACILITY".info -t omsagent "[INFO] $1" } log_warning() { echo -e "warning\t$1" logger -i -p "$LOG_FACILITY".warning -t omsagent "[WARNING] $1" } log_error() { echo -e "error\t$1" logger -i -p "$LOG_FACILITY".err -t omsagent "[ERROR] $1" } parse_args() { local OPTIND opt while getopts "h?s:w:d:vp:u:a:lx:XUm:oR" opt; do case "$opt" in h|\?) usage clean_exit 0 ;; s) ONBOARDING=1 SHARED_KEY=$OPTARG ;; w) ONBOARDING=1 WORKSPACE_ID=$OPTARG ;; d) ONBOARDING=1 URL_TLD=$OPTARG ;; v) VERBOSE=1 CURL_VERBOSE=-v ;; p) PROXY_HOST=$OPTARG ;; u) PROXY_USER=$OPTARG ;; a) AZURE_RESOURCE_ID=$OPTARG ;; l) LIST_WORKSPACES=1 ;; x) REMOVE=1 WORKSPACE_ID=$OPTARG ;; X) REMOVE_ALL=1 ;; U) UPDATE_WORKSPACES=1 ;; m) MULTI_HOMING_MARKER=$OPTARG ;; o) DETECT_SCOM=1 ;; R) RECONSTRUCT_WORKSPACE_STATE=1 ;; esac done shift $((OPTIND-1)) if [ "$@ " != " " ]; then log_error "Parsing error: '$@' is unparsed" usage clean_exit $INVALID_OPTION_PROVIDED fi if [ -n "$PROXY_USER" -a -z "$PROXY_HOST" ]; then log_error "Cannot specify the proxy user without specifying the proxy host" usage clean_exit $INVALID_PROXY fi if [ -n "$PROXY_HOST" ]; then if [ -n "$PROXY_USER" ]; then read -s -p "Proxy password for $PROXY_USER: " PROXY_PASS echo create_proxy_conf "$PROXY_USER:$PROXY_PASS@$PROXY_HOST" else create_proxy_conf "$PROXY_HOST" fi fi if [ $VERBOSE -eq 0 ]; then # Suppress curl output CURL_VERBOSE=-s fi } create_proxy_conf() { local conf_proxy_content=$1 touch $CONF_PROXY chown_omsagent $CONF_PROXY chmod 600 $CONF_PROXY echo -n $conf_proxy_content > $CONF_PROXY log_info "Created proxy configuration: $CONF_PROXY" } copy_proxy_conf_from_pre_mh_loc() { # Expected behavior is to use new proxy location first, then fallback to # the pre-multi-homing location, then assume no proxy is configured # In future: change from cp to mv once all plugins have been updated to use new location if [ -f "$PRE_MH_CONF_PROXY" ]; then echo "Moving proxy configuration from file $PRE_MH_CONF_PROXY to $CONF_PROXY..." cp -pf $PRE_MH_CONF_PROXY $CONF_PROXY fi } set_proxy_setting() { if [ -n "$PROXY" ]; then PROXY_SETTING="--proxy $PROXY" create_proxy_conf "$PROXY" return fi local conf_proxy_content="" [ -r "$CONF_PROXY" ] && conf_proxy_content=`cat $CONF_PROXY` if [ -n "$conf_proxy_content" ]; then PROXY_SETTING="--proxy $conf_proxy_content" log_info "Using proxy settings from '$CONF_PROXY'" fi } is_scom_port_open() { $OMI_CONF_EDITOR httpsport -q 1270 < $OMI_CONF_FILE > /dev/null 2>&1 if [ $? -eq 1 ]; then return 1 fi echo "Port 1270 already open" return 0 } onboard_scom() { echo "Onboarding SCOM" create_workspace_directories "scom" if [ ! -f $CERT_DIR/scom-cert.pem ] || [ ! -f $CERT_DIR/scom-key.pem ]; then echo "Generating SCOM certificates..." # Generate client auth cert using scxsslconfig tool. $SCX_SSL_CONFIG -c -g $CERT_DIR if [ $? -ne 0 ]; then log_error "Error generating certs" clean_exit 1 fi # Rename cert/key to more meaningful name mv $CERT_DIR/omi-host-*.pem $CERT_DIR/scom-cert.pem mv $CERT_DIR/omikey.pem $CERT_DIR/scom-key.pem rm -f $CERT_DIR/omi.pem chown_omsagent $CERT_DIR/scom-cert.pem chown_omsagent $CERT_DIR/scom-key.pem fi touch $CONF_DIR/omsagent.conf update_path $CONF_DIR/omsagent.conf chown_omsagent $CONF_DIR/* make_dir $CONF_DIR/omsagent.d #Always register SCOM as secondary Workspace echo "SCOM Workspace" > $CONF_DIR/.multihoming_marker configure_logrotate #Open port 1270 if not already open is_scom_port_open if [ $? -eq 1 ]; then echo "Opening port 1270" $OMI_CONF_EDITOR httpsport -a 1270 < $OMI_CONF_FILE > /etc/opt/omi/conf/omiserver.conf_temp mv /etc/opt/omi/conf/omiserver.conf_temp $OMI_CONF_FILE # Restart OMI /opt/omi/bin/service_control restart fi } onboard_lad() { echo "Onboarding LAD" create_workspace_directories "LAD" touch $CONF_DIR/omsagent.conf echo "@include omsagent.d/*" > $CONF_DIR/omsagent.conf chown_omsagent $CONF_DIR/* make_dir $CONF_DIR/omsagent.d #Always register LAD as secondary Workspace echo "LAD Workspace" > $CONF_DIR/.multihoming_marker save_config configure_logrotate } onboard() { if [ "$METACONFIG_PY" = "" ]; then echo "Error: Python is not installed on this system, onboarding cannot continue." >&2 echo "Please install either the python2 or python3 package." >&2 clean_exit $INSTALL_PYTHON fi if [ $VERBOSE -eq 1 ]; then # Mask the shared key local shared_key_trunc=`echo "$SHARED_KEY" | cut -c 1-4 2> /dev/null` local shared_key_covered=`echo "$SHARED_KEY" | tr "$SHARED_KEY" "*" | cut -c 5- 2> /dev/null` echo "Workspace ID: $WORKSPACE_ID" echo "Shared key: $shared_key_trunc$shared_key_covered" echo "Top Level Domain: $URL_TLD" fi if [ "$WORKSPACE_ID" = "scom" ]; then onboard_scom clean_exit $? fi if [ "$WORKSPACE_ID" = "LAD" ]; then onboard_lad clean_exit $? fi local error=0 if [ -z "$WORKSPACE_ID" -o -z "$SHARED_KEY" ]; then log_error "Missing Workspace ID or Shared Key information for onboarding" clean_exit $INVALID_CONFIG_PROVIDED fi if echo "$WORKSPACE_ID" | grep -Eqv '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$'; then log_error "The Workspace ID is not valid" clean_exit $INVALID_CONFIG_PROVIDED fi if [ ! -z "$AZURE_RESOURCE_ID" ]; then update_azure_resource_id fi # If a test is not in progress then call service_control to check on the workspace status if [ -z "$TEST_WORKSPACE_ID" -a -z "$TEST_SHARED_KEY" ]; then $SERVICE_CONTROL is-running $WORKSPACE_ID > /dev/null 2>&1 if [ $? -eq 1 ]; then echo "Workspace $WORKSPACE_ID already onboarded and agent is running." if [ -z "$MULTI_HOMING_MARKER" -a ! -h $DF_CONF_DIR ]; then echo "Symbolic links have not been created; re-onboarding to create them" else return 0 fi fi fi create_workspace_directories $WORKSPACE_ID # Guard against blank omsadmin.conf local omsadmin_contents="`cat $CONF_OMSADMIN 2> /dev/null`" if [ -f $FILE_KEY -a -f $FILE_CRT -a -f $CONF_OMSADMIN -a -n "$omsadmin_contents" ]; then # Keep the same agent GUID by loading it from the previous conf AGENT_GUID=`grep AGENT_GUID $CONF_OMSADMIN | cut -d= -f2` log_info "Reusing previous agent GUID $AGENT_GUID" else AGENT_GUID=`$RUBY -e "require 'securerandom'; print SecureRandom.uuid"` if [ $? -ne 0 -o -z "$AGENT_GUID" ]; then log_error "Error generating agent GUID" clean_exit $RUBY_ERROR_GENERATING_GUID fi $RUBY $MAINTENANCE_TASKS_SCRIPT -c "$CONF_OMSADMIN" "$FILE_CRT" "$FILE_KEY" "$RUN_DIR/omsagent.pid" "$CONF_PROXY" "$OS_INFO" "$INSTALL_INFO" -w "$WORKSPACE_ID" -a "$AGENT_GUID" $CURL_VERBOSE generate_certs_ret=$? if [ $generate_certs_ret -eq $AGENT_MAINTENANCE_MISSING_CONFIG ]; then log_error "Error generating certs: missing config" clean_exit $INTERNAL_ERROR elif [ $generate_certs_ret -ne 0 ]; then log_error "Error generating certs" clean_exit $ERROR_GENERATING_CERTS fi fi if [ -z "$AGENT_GUID" ]; then log_error "AGENT_GUID should not be empty" return $INTERNAL_ERROR else log_info "Agent GUID is $AGENT_GUID" fi if [ "$VERBOSE" = "1" ]; then log_info "Private Key stored in: $FILE_KEY" log_info "Public Key stored in: $FILE_CRT" # Public key can be verified with a command like: openssl x509 -text -in oms.crt fi # Generate the body first so we can compute a SHA256 on the body REQ_DATE=`date +%Y-%m-%dT%T.%N%:z` CERT_SERVER=`cat $FILE_CRT | awk 'NR>2 { print line } { line = $0 }'` # append telemetry to $BODY_ONBOARD `$RUBY $TOPOLOGY_REQ_SCRIPT -t "$BODY_ONBOARD" "$OS_INFO" "$CONF_OMSADMIN" "$AGENT_GUID" "$CERT_SERVER" "$RUN_DIR/omsagent.pid"` [ $? -ne 0 ] && log_error "Error appending Telemetry during Onboarding." cat /dev/null > "$SHARED_KEY_FILE" chmod 600 "$SHARED_KEY_FILE" echo -n "$SHARED_KEY" >> "$SHARED_KEY_FILE" AUTHS=`$RUBY $AUTH_KEY_SCRIPT $REQ_DATE $BODY_ONBOARD $SHARED_KEY_FILE` rm "$SHARED_KEY_FILE" CONTENT_HASH=`echo $AUTHS | cut -d" " -f1` AUTHORIZATION_KEY=`echo $AUTHS | cut -d" " -f2` if [ $VERBOSE -ne 0 ]; then # Mask the certificate in the request local body_no_newlines="`cat $BODY_ONBOARD | tr -d '\n' 2> /dev/null`" local cert_tag="AuthenticationCertificate" local hidden_cert="********" echo echo "Generated request:" echo "$body_no_newlines" | sed "s/<$cert_tag>[^<]*<\/$cert_tag>/<$cert_tag>$hidden_cert<\/$cert_tag>/g" echo fi set_proxy_setting if [ "`which dmidecode > /dev/null 2>&1; echo $?`" = 0 ]; then UUID=`dmidecode | grep UUID | sed -e 's/UUID: //'` OMSCLOUD_ID=`dmidecode | grep "Tag: 77" | sed -e 's/Asset Tag: //'` elif [ -f /sys/devices/virtual/dmi/id/chassis_asset_tag ]; then ASSET_TAG=$(cat /sys/devices/virtual/dmi/id/chassis_asset_tag) case "$ASSET_TAG" in 77*) OMSCLOUD_ID=$ASSET_TAG ;; # If the asset tag begins with a 77 this is the azure guid esac PRODUCT_UUID=$(cat /sys/devices/virtual/dmi/id/product_uuid) UUID=$(echo $PRODUCT_UUID | tr '[:upper:]' '[:lower:]') fi #This is a temporary fix for Systems with Curl versions using HTTP\2 as default #Since 7.47.0, the curl tool enables HTTP/2 by default for HTTPS connections. This fix runs curl with --http1.1 on systems with version above 7.47.0 #Curl http2 Docs Link: https://curl.haxx.se/docs/http2.html CURL_VERSION_WITH_DEFAULT_HTTP2="7470" CURL_VERSION_SYSTEM=`curl --version | head -c11 | awk '{print $2}' | tr --delete .` if [ $CURL_VERSION_SYSTEM -gt $CURL_VERSION_WITH_DEFAULT_HTTP2 ]; then CURL_HTTP_COMMAND="--http1.1" fi RET_CODE=`curl --header "x-ms-Date: $REQ_DATE" \ --header "x-ms-version: August, 2014" \ --header "x-ms-SHA256_Content: $CONTENT_HASH" \ --header "Authorization: $WORKSPACE_ID; $AUTHORIZATION_KEY" \ --header "User-Agent: $USER_AGENT" \ --header "Accept-Language: en-US" \ --insecure \ $CURL_HTTP_COMMAND \ --data-binary @$BODY_ONBOARD \ --cert "$FILE_CRT" --key "$FILE_KEY" \ --output "$RESP_ONBOARD" $CURL_VERBOSE \ --write-out "%{http_code}\n" $PROXY_SETTING \ https://${WORKSPACE_ID}.oms.${URL_TLD}/AgentService.svc/LinuxAgentTopologyRequest` || error=$? if [ $error -eq $CURL_HOST_RESOLVE_ERROR -a -z "$PROXY_SETTING" ]; then log_error "Error resolving host during the onboarding request. Check the correctness of the workspace ID and the internet connectivity, or add a proxy." cleanup_certs return $ERROR_RESOLVING_HOST elif [ $error -eq $CURL_PROXY_RESOLVE_ERROR -a -n "$PROXY_SETTING" ]; then log_error "Proxy could not be resolved during the onboarding request: curl returned $error. Verify the proxy." cleanup_certs return $INVALID_PROXY elif [ $error -eq $CURL_CONNECT_HOST_ERROR -a -n "$PROXY_SETTING" ]; then log_error "Error connecting to OMS service through proxy: curl returned $error. Verify the proxy." cleanup_certs return $INVALID_PROXY elif [ $error -ne 0 ]; then log_error "Error during the onboarding request: curl returned $error. Check the internet connectivity and the correctness of the workspace ID and shared key or run omsadmin.sh with '-v'" cleanup_certs return $ERROR_ONBOARDING fi if [ "$RET_CODE" = "200" ]; then $RUBY $MAINTENANCE_TASKS_SCRIPT --endpoints "$RESP_ONBOARD","$ENDPOINT_FILE" "$CONF_OMSADMIN" "$FILE_CRT" "$FILE_KEY" "$RUN_DIR/omsagent.pid" "$CONF_PROXY" "$OS_INFO" "$INSTALL_INFO" $CURL_VERBOSE endpoints_ret=$? if [ $endpoints_ret -eq $AGENT_MAINTENANCE_MISSING_CONFIG_FILE ]; then log_error "Onboarding response is missing; certificate update and DSC endpoints were not extracted." elif [ $endpoints_ret -eq $AGENT_MAINTENANCE_ERROR_WRITING_TO_FILE ]; then log_error "Endpoint file could not be written to; certificate update and DSC endpoints were not saved." elif [ $endpoints_ret -ne 0 ]; then log_warning "During onboarding request, certificate update and DSC endpoints may not have been saved." else log_info "Onboarding success" fi # Get CERTIFICATE_UPDATE_ENDPOINT and DSC_ENDPOINT variables from output file if [ -e "$ENDPOINT_FILE" ]; then CERTIFICATE_UPDATE_ENDPOINT=`sed -n '1p' < $ENDPOINT_FILE` DSC_ENDPOINT=`sed -n '2p' < $ENDPOINT_FILE` fi elif [ "$RET_CODE" = "403" ]; then REASON=`cat $RESP_ONBOARD | sed -n 's:.*\(.*\).*:\1:p'` log_error "Error onboarding. HTTP code 403, Reason: $REASON. Check the Workspace ID, Workspace Key and that the time of the system is correct." cleanup_certs return $ERROR_ONBOARDING_403 else log_error "Error onboarding. HTTP code $RET_CODE" cleanup_certs return $ERROR_ONBOARDING_NON_200_HTTP fi save_config copy_omsagent_conf if [ -z "$MULTI_HOMING_MARKER" ]; then # update the default folders when onboard to a workspace as primary # this is the default behavior update_symlinks else # do not update the default folders when onboard the workspace as secondary # leave a marker in the etc folder of the workspace to indicate the partner echo $MULTI_HOMING_MARKER > $CONF_DIR/.multihoming_marker fi #Initialize empty syslog daemon conf file with no default collection configure_syslog configure_monitor_agent configure_logrotate # If a test is not in progress then register omsagent as a service and start the agent if [ -z "$TEST_WORKSPACE_ID" -a -z "$TEST_SHARED_KEY" ]; then $SERVICE_CONTROL start $WORKSPACE_ID if [ -z "$MULTI_HOMING_MARKER" ]; then # Configure omsconfig when the workspace is primary # This is a temp solution since the DSC doesn't support multi-homing now # Only the primary workspace receives the configuration from the DSC service # Set up a cron job to run the OMSConsistencyInvoker every 15 minutes # This should be done regardless of MetaConfig creation if [ ! -f /etc/cron.d/OMSConsistencyInvoker ]; then randomNo=$(od -An -N1 -i /dev/urandom) A=$(($randomNo%13+2)) B=$(($A+15)) C=$(($B+15)) D=$(($C+15)) echo "$A,$B,$C,$D * * * * $AGENT_USER /opt/omi/bin/OMSConsistencyInvoker >/dev/null 2>&1" > /etc/cron.d/OMSConsistencyInvoker fi if [ ! -f $METACONFIG_PY ]; then log_error "MetaConfig generation script not available at $METACONFIG_PY. For more details check logs in /var/opt/microsoft/omsconfig/omsconfig.log" return $ERROR_METACONFIG_PY_NOT_PRESENT fi if [ "$USER_ID" -eq "0" ]; then su - $AGENT_USER -c $METACONFIG_PY > /dev/null || error=$? if [ $error -ne 0 ]; then log_info "Retrying configuring omsconfig by calling '$METACONFIG_PY' with 'sudo'" error=0 sudo -u $AGENT_USER $METACONFIG_PY > /dev/null || error=$? fi else $METACONFIG_PY > /dev/null || error=$? fi if [ $error -eq 0 ]; then log_info "omsconfig is configured successfuly" else log_error "Configuring omsconfig and generating metaconfig failed with error code $error. For more details check logs in /var/opt/microsoft/omsconfig/omsconfig.log" return $ERROR_GENERATING_METACONFIG fi fi fi return 0 } remove_workspace() { setup_workspace_variables $WORKSPACE_ID if [ -d "$CONF_DIR" ]; then log_info "Disable workspace: $WORKSPACE_ID" $SERVICE_CONTROL disable $WORKSPACE_ID else log_error "Workspace $WORKSPACE_ID doesn't exist" fi log_info "Cleanup the folders" local port=$DEFAULT_SYSLOG_PORT if [ -e "$CONF_DIR/omsagent.d/syslog.conf" ]; then port=`grep 'port .*' $CONF_DIR/omsagent.d/syslog.conf | cut -d ' ' -f4` fi /opt/microsoft/omsagent/bin/configure_syslog.sh unconfigure $WORKSPACE_ID ${port} if [ -f "$NPM_CONF_WS" -a -d "$NPM_DIR" ]; then log_info "Removing NPM state directory in $WORKSPACE_ID removal" rm -rf $NPM_DIR > /dev/null 2>&1 fi rm -rf "$VAR_DIR_WS" "$ETC_DIR_WS" > /dev/null 2>&1 rm -f /etc/logrotate.d/omsagent-$WORKSPACE_ID > /dev/null 2>&1 reset_default_workspace } reset_default_workspace() { if [ -h $DF_CONF_DIR -a ! -d $DF_CONF_DIR ]; then # default conf folder is removed, remove the symlinks rm "$DF_TMP_DIR" "$DF_RUN_DIR" "$DF_STATE_DIR" "$DF_LOG_DIR" "$DF_CERT_DIR" "$DF_CONF_DIR" > /dev/null 2>&1 fi } remove_all() { for ws_id in `ls -1 $ETC_DIR | grep -E '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$'` do WORKSPACE_ID=${ws_id} remove_workspace done # Remove non-OMS "workspaces" for ws_id in $NON_OMS_WS do ls -l $ETC_DIR | grep -w "$ws_id" > /dev/null 2>&1 if [ $? -eq 0 ]; then WORKSPACE_ID="$ws_id" remove_workspace fi done } show_workspace_status() { local ws_conf_dir=$1 local ws_id=$2 local is_primary=$3 local status='Unknown' # 1 if omsagent-ws_id is running, 0 otherwise $SERVICE_CONTROL is-running $ws_id if [ $? -eq 1 ]; then status='Onboarded(OMSAgent Running)' elif [ -f ${ws_conf_dir}/.service_registered ]; then status='Warning(OMSAgent Registered, Not Running)' elif [ -f ${ws_conf_dir}/omsadmin.conf ]; then status='Saved(OMSAgent Not Registered, Workspace Configuration Saved)' else status='Failure(Agent Not Onboarded, No Workspace Configuration Present)' fi local mh_marker= if [ -f ${ws_conf_dir}/.multihoming_marker ]; then mh_marker="(`cat ${ws_conf_dir}/.multihoming_marker`)" fi if [ ${is_primary} -eq 1 ]; then echo "Primary Workspace: ${ws_id} Status: ${status}" else echo "Workspace${mh_marker}: ${ws_id} Status: ${status}" fi } list_scom_workspace() { ls -1 $ETC_DIR | grep -w scom > /dev/null 2>&1 if [ $? -eq 0 ]; then show_workspace_status $ETC_DIR/scom/conf scom 0 return 1 fi return 0 } list_workspaces() { local found_ws=0 local ws_conf_dir=$ETC_DIR/conf if [ -h ${ws_conf_dir} ]; then # symbolic link - multiple workspace folder structure local primary_ws_id='' if [ -f ${ws_conf_dir}/omsadmin.conf ]; then primary_ws_id=`grep WORKSPACE_ID ${ws_conf_dir}/omsadmin.conf | cut -d= -f2` fi if [ "${primary_ws_id}" != "" ]; then found_ws=1 show_workspace_status ${ws_conf_dir} ${primary_ws_id} 1 fi for ws_id in `ls -1 $ETC_DIR | grep -E '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$'` do if [ "${primary_ws_id}" != "${ws_id}" ]; then found_ws=1 show_workspace_status $ETC_DIR/${ws_id}/conf ${ws_id} 0 fi done elif [ -d ${ws_conf_dir} ]; then # directory - single workspace folder structure local ws_id='' if [ -f ${ws_conf_dir}/omsadmin.conf ]; then ws_id=`grep WORKSPACE_ID ${ws_conf_dir}/omsadmin.conf | cut -d= -f2` fi if [ "${ws_id}" != "" ]; then found_ws=1 show_workspace_status ${ws_conf_dir} ${ws_id} 1 fi else # no default conf folder, check all the potential workspace folders for ws_id in `ls -1 $ETC_DIR | grep -E '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$'` do found_ws=1 show_workspace_status $ETC_DIR/${ws_id}/conf ${ws_id} 0 done fi # check scom workspace list_scom_workspace if [ $? -eq 1 ]; then found_ws=1 fi if [ $found_ws -eq 0 ]; then echo "No Workspace" fi return 0 } migrate_pre_mh_workspace() { if [ -d $DF_CONF_DIR -a ! -h $DF_CONF_DIR -a -f $DF_CONF_DIR/omsadmin.conf ]; then WORKSPACE_ID=`grep WORKSPACE_ID $DF_CONF_DIR/omsadmin.conf | cut -d= -f2` if [ $? -ne 0 -o -z $WORKSPACE_ID ]; then echo "WORKSPACE_ID is not found. Skip migration." return else echo "Migrating to multi-homing folder structure..." fi create_workspace_directories $WORKSPACE_ID cp -rpf $DF_TMP_DIR $VAR_DIR_WS cp -rpf $DF_STATE_DIR $VAR_DIR_WS cp -rpf $DF_RUN_DIR $VAR_DIR_WS cp -rpf $DF_LOG_DIR $VAR_DIR_WS cp -rpf $DF_CERT_DIR $ETC_DIR_WS cp -rpf $DF_CONF_DIR $ETC_DIR_WS copy_omsagent_d_conf migrate_pre_mh_omsagent_conf sed -i s,%SYSLOG_PORT%,$DEFAULT_SYSLOG_PORT,1 $CONF_DIR/omsagent.d/syslog.conf sed -i s,%MONITOR_AGENT_PORT%,$DEFAULT_MONITOR_AGENT_PORT,1 $CONF_DIR/omsagent.d/monitor.conf update_symlinks elif [ -d $DF_CONF_DIR -a ! -h $DF_CONF_DIR ]; then # In some upgrade cases, conf and conf/omsagent.d directories are created and are empty # Remove these directories if they are empty rmdir $DF_CONF_DIR/omsagent.d 2> /dev/null rmdir $DF_CONF_DIR 2> /dev/null fi } migrate_pre_mh_omsagent_conf() { # migrate the omsagent.conf cp -pf $CONF_DIR/omsagent.conf $CONF_DIR/omsagent.conf.bak # remove the syslog configuration. it has been moved to omsagent.d/syslog.conf cat $CONF_DIR/omsagent.conf.bak | sed '/port 25224/,+4 d' | sed '//,+3 d' | tac | sed '/type syslog/,+1 d' | tac > $CONF_DIR/omsagent.conf # update the heartbeat configure to use the fake command sed -i s,"command /opt/microsoft/omsagent/bin/omsadmin.sh -b > /dev/null","command echo > /dev/null",1 $CONF_DIR/omsagent.conf # add the workspace conf, cert and key to the output plugins sed -i s,".*buffer_chunk_limit.*","\n omsadmin_conf_path $CONF_DIR/omsadmin.conf\n cert_path $CERT_DIR/oms.crt\n key_path $CERT_DIR/oms.key\n\n&",g $CONF_DIR/omsagent.conf # update the workspace state folder sed -i s,"/var/opt/microsoft/omsagent/state",$STATE_DIR,g $CONF_DIR/omsagent.conf } update_omsagent_d_conf() { # Parameter: Workspace ID for the folder to update setup_workspace_variables $1 WS_OMSAGENT_D_DIR=$CONF_DIR/omsagent.d if [ ! -d "$WS_OMSAGENT_D_DIR" ]; then echo "$WS_OMSAGENT_D_DIR does not exist; skipping update in this directory." return fi # Note: if syslog.conf, monitor.conf, or other configuration files using a port must be updated # during version upgrade, then new functionality will have to be added here copy_no_port_omsagent_d_conf $WS_OMSAGENT_D_DIR chown_omsagent $WS_OMSAGENT_D_DIR/* } # Update configuration and structure for all configured workspaces update_workspaces() { local error=0 # Updating from pre-multi-homing version migrate_pre_mh_workspace || error=$? # Updating from pre-multi-homing structure (somewhat present in versions >1.3) copy_proxy_conf_from_pre_mh_loc || error=$? # Updating configuration for all onboarded workspaces to latest shipped versions for ws_id in `ls -1 $ETC_DIR | grep -E '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$'` do update_omsagent_d_conf $ws_id || error=$? done if [ $error -ne 0 ]; then clean_exit $error fi } reconstruct_full_workspace_state() { for ws_id in `ls -1 $ETC_DIR | grep -E '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$'` do WORKSPACE_ID=$ws_id setup_workspace_variables $WORKSPACE_ID if [ -f $CERT_DIR/oms.key -a -f $CERT_DIR/oms.crt -a -f $CONF_DIR/omsadmin.conf ]; then local omsadmin_contents="`cat $CONF_DIR/omsadmin.conf 2> /dev/null`" if [ -n "$omsadmin_contents" ]; then # Create all workspace-specific directories; if they already exist, this is a NOOP create_workspace_directories $WORKSPACE_ID # If a test is not in progress, then syslog and logrotate can be set up if [ -z "$TEST_WORKSPACE_ID" -a -z "$TEST_SHARED_KEY" ]; then # During omsagent removal, log rotate files are removed; set up log rotate like in onboarding configure_logrotate # Note: we could re-configure syslog here, but it will overwrite user settings in some cases fi else log_warning "Workspace $ws_id has an empty configuration file at $CONF_DIR/omsadmin.conf; please onboard to populate this configuration" fi else log_warning "Workspace $ws_id has created a folder $ETC_DIR/$ws_id, but is missing certificates or configuration; please onboard" fi done } setup_workspace_variables() { VAR_DIR_WS=$VAR_DIR/$1 ETC_DIR_WS=$ETC_DIR/$1 TMP_DIR=$VAR_DIR_WS/tmp STATE_DIR=$VAR_DIR_WS/state RUN_DIR=$VAR_DIR_WS/run LOG_DIR=$VAR_DIR_WS/log CERT_DIR=$ETC_DIR_WS/certs CONF_DIR=$ETC_DIR_WS/conf NPM_CONF_WS=$ETC_DIR/$1/$NPM_CONF_FILE_SUFFIX } create_workspace_directories() { setup_workspace_variables $1 make_dir $VAR_DIR_WS make_dir $ETC_DIR_WS make_dir $TMP_DIR make_dir $STATE_DIR make_dir $RUN_DIR make_dir $LOG_DIR make_dir $CERT_DIR make_dir $CONF_DIR chmod 700 $CERT_DIR # Generated conf file containing information for this script CONF_OMSADMIN=$CONF_DIR/omsadmin.conf # Omsagent daemon configuration CONF_OMSAGENT=$CONF_DIR/omsagent.conf # Certs FILE_KEY=$CERT_DIR/oms.key FILE_CRT=$CERT_DIR/oms.crt # Temporary files SHARED_KEY_FILE=$TMP_DIR/shared_key BODY_ONBOARD=$TMP_DIR/body_onboard.xml RESP_ONBOARD=$TMP_DIR/resp_onboard.xml ENDPOINT_FILE=$TMP_DIR/endpoints } make_dir() { if [ ! -d $1 ]; then mkdir -m 750 $1 else if [ $VERBOSE -eq 1 ]; then echo "Directory $1 already exists." fi chmod 750 $1 fi chown_omsagent $1 } copy_omsagent_conf() { cp -p $SYSCONF_DIR/omsagent.conf $CONF_DIR update_path $CONF_DIR/omsagent.conf chown_omsagent $CONF_DIR/* copy_omsagent_d_conf } copy_omsagent_d_conf() { OMSAGENTD_DIR=$CONF_DIR/omsagent.d make_dir $OMSAGENTD_DIR cp -p $SYSCONF_DIR/omsagent.d/monitor.conf $OMSAGENTD_DIR cp -p $SYSCONF_DIR/omsagent.d/syslog.conf $OMSAGENTD_DIR update_path $OMSAGENTD_DIR/monitor.conf copy_no_port_omsagent_d_conf $OMSAGENTD_DIR chown_omsagent $OMSAGENTD_DIR/* } copy_no_port_omsagent_d_conf() { # Copy configuration files from sysconf to a workspace-specific omsagent.d directory # which do not depend on a workspace-specific port being set # Parameter: workspace-specific omsagent.d directory cp -p $SYSCONF_DIR/omsagent.d/heartbeat.conf $1 cp -p $SYSCONF_DIR/omsagent.d/telemetry.conf $1 cp -p $SYSCONF_DIR/omsagent.d/operation.conf $1 cp -p $SYSCONF_DIR/omi_mapping.json $1 cp -p $SYSCONF_DIR/omsagent.d/container.conf $1 2> /dev/null update_path $1/heartbeat.conf update_path $1/telemetry.conf update_path $1/operation.conf if [ -f $1/container.conf ] ; then update_path $1/container.conf fi } update_path() { sed -i s,%CONF_DIR_WS%,$CONF_DIR,1 $1 sed -i s,%CERT_DIR_WS%,$CERT_DIR,1 $1 sed -i s,%TMP_DIR_WS%,$TMP_DIR,1 $1 sed -i s,%RUN_DIR_WS%,$RUN_DIR,1 $1 sed -i s,%STATE_DIR_WS%,$STATE_DIR,1 $1 sed -i s,%LOG_DIR_WS%,$LOG_DIR,1 $1 } update_symlinks() { link_dir $DF_TMP_DIR $TMP_DIR link_dir $DF_RUN_DIR $RUN_DIR link_dir $DF_STATE_DIR $STATE_DIR link_dir $DF_LOG_DIR $LOG_DIR link_dir $DF_CERT_DIR $CERT_DIR link_dir $DF_CONF_DIR $CONF_DIR } link_dir() { if [ -d $1 ]; then rm -r $1 fi ln -s $2 $1 chown_omsagent $1 } find_available_port() { local port=$1 local result=$2 if [ "`which netstat > /dev/null 2>&1; echo $?`" = 0 ]; then until [ -z "`netstat -an | grep ${port}`" -a -z "`grep ${port} $ETC_DIR/*/conf/omsagent.d/*.conf`" ]; do port=$((port+1)) done else log_info "netstat tool is not available. Default port defined in omsadmin.sh will be used." fi eval $result="${port}" } configure_syslog() { echo "Configure syslog..." find_available_port $DEFAULT_SYSLOG_PORT SYSLOG_PORT sed -i s,%SYSLOG_PORT%,$SYSLOG_PORT,1 $CONF_DIR/omsagent.d/syslog.conf /opt/microsoft/omsagent/bin/configure_syslog.sh configure $WORKSPACE_ID $SYSLOG_PORT } configure_monitor_agent() { echo "Configure heartbeat monitoring agent..." find_available_port $DEFAULT_MONITOR_AGENT_PORT MONITOR_AGENT_PORT sed -i s,%MONITOR_AGENT_PORT%,$MONITOR_AGENT_PORT,1 $CONF_DIR/omsagent.d/monitor.conf } configure_logrotate() { echo "Configure log rotate for workspace $WORKSPACE_ID..." # create the logrotate file for omsagent if workspace is LAD if [ "$WORKSPACE_ID" = "LAD" ]; then # create the logrotate file for the LAD if it doesn't exist if [ ! -f /etc/logrotate.d/omsagent-$WORKSPACE_ID ]; then cp $SYSCONF_DIR/lad-logrotate.conf /etc/logrotate.d/omsagent-$WORKSPACE_ID fi else # create the logrotate file for the workspace if it doesn't exist if [ ! -f /etc/logrotate.d/omsagent-$WORKSPACE_ID ]; then cat $SYSCONF_DIR/logrotate.conf | sed "s/%WORKSPACE_ID%/$WORKSPACE_ID/g" > /etc/logrotate.d/omsagent-$WORKSPACE_ID fi fi # Label omsagent log files according to selinux policy module for logrotate if selinux is present SEPKG_DIR_OMSAGENT=/usr/share/selinux/packages/omsagent-logrotate if [ -e /usr/sbin/semodule -a -d "$SEPKG_DIR_OMSAGENT" ]; then # Label omsagent log file for this $WORKSPACE_ID /sbin/restorecon -R $VAR_DIR/*/log > /dev/null 2>&1 fi } main() { check_user set_user_agent parse_args $@ if [ $# -eq 0 ] || [ $# -eq 1 -a "$VERBOSE" = "1" ]; then # Allow onboarding params to be loaded from a file # The file contains at least these two lines : # WORKSPACE_ID="[...]" # SHARED_KEY="[...]" if [ -r "$FILE_ONBOARD" ]; then log_info "Reading onboarding params from: $FILE_ONBOARD" . "$FILE_ONBOARD" ONBOARDING=1 ONBOARD_FROM_FILE=1 else usage clean_exit $INVALID_OPTION_PROVIDED fi fi if [ "$DETECT_SCOM" = "1" ]; then is_scom_port_open > /dev/null 2>&1 clean_exit $? fi if [ "$ONBOARDING" = "1" ]; then onboard || clean_exit $? fi if [ "$LIST_WORKSPACES" = "1" ]; then list_workspaces || clean_exit 1 fi if [ "$UPDATE_WORKSPACES" = "1" ]; then update_workspaces || clean_exit $? fi if [ "$RECONSTRUCT_WORKSPACE_STATE" = "1" ]; then reconstruct_full_workspace_state || clean_exit $? fi if [ "$REMOVE" = "1" ]; then remove_workspace || clean_exit 1 fi if [ "$REMOVE_ALL" = "1" ]; then remove_all || clean_exit 1 fi # If we reach this point, onboarding was successful, we can remove the # onboard conf to prevent accidentally re-onboarding [ "$ONBOARD_FROM_FILE" = "1" ] && rm "$FILE_ONBOARD" > /dev/null 2>&1 || true clean_exit 0 } main $@