はじめに
z/OSのJOBLOGをSlackに送信するってのをやっている人がいました。
こちら=> zOS_Joblog_to_Slack
Slackにメッセージ送信するのは結構簡単にできそうだったので、自分でもやってみようかなと思い、試してみました。
上のサンプルはJavaからSlackのAPIを発行するようになっていますが、個人的にコンパイラ言語を使うのが最近は鬱陶しく感じられるので、もうちょっとお手軽に試せるように、USS上のシェル・スクリプトからSlackにメッセージを送信するための部品を作ってみました。
参考情報
zOS_Joblog_to_Slack
Slack API
Slack API- Messaging
Slack API - Your Apps
Slackにファイルをアップロードする
Slack でのIncoming Webhook の利用
全体像
前提
Slack API
Web APIが使えるようにする必要があります。試した手順は以下に記載しますが、メッセージを送付したいワークスペースにアプリを作成して、Web API発行時に指定するTokenを取得するというようなことをやる必要があります。
冒頭のリンクのサンプルではIncomming Webhookという簡易的にメッセージ送信を行える仕組みを使っているようですが、ここではIncomming Webhookではなく、chat.postMessage, files.uploadを利用しています。
参考: Sending messages - Many ways to send messages
z/OS USS
ここではUSS(Unix System Service)上のシェルからSlackにメッセージを送信することを想定しています。(以前作成したLightweight Command Utility on z/OS USSと連携して活用することを念頭においているので。)
シェル・スクリプトからSlackのAPIを手っ取り早く発行する方法として、curlコマンドを使います(シェルからHTTPのリクエストを発行するためのコマンド)。ただし、curlコマンドはUSSではデフォルトでは提供されていないようで、Rocket社提供のものをインストールしておく必要があります。以下からフリーで入手可能です(要ユーザー登録)。
参考: cURL for z/OS
今回試した環境
z/OS V2.3
cURL for z/OS V7.52.1
[CICS004@EPLEX1:/] curl --version
curl 7.52.1 (i370-ibm-openedition) libcurl/7.52.1 OpenSSL/1.0.2l zlib/1.2.11 libssh2/1.8.0 nghttp2/1.18.1
Protocols: dict file ftp ftps gopher http https imap imaps pop3 pop3s rtsp scp sftp smb smbs smtp smtps telnet tftp
Features: IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz TLS-SRP HTTP2 UnixSockets HTTPS-proxy Metalink
※余談: なるべく追加のコンポーネントは無しでやりたかったのですが、curlくらいはあってもいいと思うし、お手軽さを考えて、今回はこれを前提とします。というか、API化だのモダナイゼーションだのデジタルトランスフォーメーションだのと言ってる割りに、こういうツールがデフォルトで使えるようになっていないというのがイケてないんだよなぁ。curlとかgzipとかbashとか、オープン系だとごくふつーーーーに使えるものが使えないってのが、本当にフラストレーション溜まります。こういうのがデフォルトで使えるだけで、いろんなものとの連携が格段にやりやすくなって世界が広がると思うんですけど。色々大仰なことをやる前にこういうところ整備するのが先だと思うんだけどなぁ。
また、メインフレームはネットワーク上セキュアな環境にあることが多いと思いますので、ダイレクトに外のサーバー(Slackのサーバー)にアクセスできないことが多いと思います。今回使っているテスト環境もダイレクトに外の環境とは接続できません。そのため、上に示した図の通り、今回は外部にアクセス可能な所に一時的にProxyサーバーを立てて、そこを経由してSlack APIを発行しています。(Proxy Server部分の詳細はここでは割愛しますが、一般的なHTTPSの通信ができるProxyがあればよいはずです。)
設定/実装
さて、ここからは実際にz/OS USS上のシェルスクリプトからSlackにメッセージを送信するまでの、一連の手順例を記載していきます。
Slack側設定
メッセージ送付対象のワークスペースやアカウントは設定済みの想定です。
(1)アプリケーション作成
以下にアクセスします。
https://api.slack.com/apps
Slackのアカウントにサインインしていないと、以下の画面になると思うので、対象のワークスペースにサインインした後、再度上のURLにアクセスします。
サインインしている場合、以下のような画面になるので、Create New Appをクリックします。
アプリの名前と対象のワークスペース名を指定してCreate Appをクリックします。
(2)権限設定
Scopeの欄の Add an OAuth Scopeをクリックします。
chat:write:userと、files:write:userを追加します。
(3)ユーザー作成
左側のメニューからBot Usersを選択して、Add a Bot Userをクリック
名前を指定して(ここではデフォルトで設定されている名前そのまま)、Add Bot Userをクリックします。
黄色い帯のメッセージのreinstall your appのリンクをクリック
アクセスTokenがアサインされるので、Bot User OAuth Access Tokenの方のTokenをメモっておきます。
(4)Slack API動作確認
上のTokenを使用してRESTのAPIを発行できるようになっているはずなので、z/OSからやるまえに、一旦ブラウザのツールなどから動作確認してみましょう。
ChromeのプラグインのTalend API Tester(昔Restlet Clientという名前だったのがいつの間にか変わってた...)を使ってやってみます。
Talend API Tester
以下のようなURIで、POSTメソッドを発行します。token=には、上で確認したtokenを、channel=にはメッセージを送付したいチャンネル名を指定します。
https://slack.com/api/chat.postMessage?token=xoxb-xxx&channel=test01&text=aaaaa
結果、以下のように意図したチャンネルにメッセージが送付されればOK!
USS側スクリプト作成
Slack APIを用いてメッセージを送信するスクリプトを作成します。
スクリプトのインターフェース:
第1引数: 送付したいメッセージを含むテキストファイル(EBCDIC)
第2引数: Slack Channel名
Slackにメッセージとして送信したいテキストをファイルとして用意し、そのファイル名と送信したいSlackのChannel名を引数に指定してスクリプトを実行すると、指定したチャネルに指定したメッセージが出力されるようにします。
2パターンのスクリプトを用意しました。
1つ目は渡したテキストをメッセージ本文(装飾つき)で送付します。
2つ目はファイルとして添付して送付します。
どちらもメッセージを送付するというのは一緒ですが、本文として送付するかファイルとして送付するかという違いです。送付するデータのサイズが大きい場合はファイルとして送付したほうがよいでしょう。
ソース中の以下の箇所は、上で取得したSlackアクセス用のtoken、および、使用するProxyサーバーのURLでそれぞれ置き換えてください。
token=xoxb-xxx
proxy=http://yyy:8080
(1)chat.postMessageメソッドによるメッセージ送信スクリプト
参考: chat.postMessage
#!/bin/sh
fileName=$1
channelName=$2
if [[ ! -e "${fileName}" ]]; then
echo "Error: file ${fileName} does noe exist."
exit 1
fi
if [[ -z ${channelName} ]]; then
channelName=general
fi
slackAPI="https://slack.com/api/chat.postMessage"
text="<!channel> Notification from z/OS !"
token=xoxb-xxx
proxy=http://yyy:8080
tempDir=/tmp
tempKey=SlackChat
pid=$$
tempFile=${tempDir}/${tempKey}_${pid}.json
tempFileUTF8=${tempDir}/${tempKey}_${pid}_utf8.json
##### Create JSON data
echo "{" > ${tempFile}
echo " \"channel\": \"${channelName}\", " >> ${tempFile}
echo " \"text\": \"${text}\", " >> ${tempFile}
echo ' "attachments": [{
"color": "good",
"text": "' >> ${tempFile}
cat ${fileName} | sed -e 's/$/ \\n/g' >> ${tempFile}
echo '"}]}' >> ${tempFile}
##### Code Conversion
iconv -f IBM-1399 -t UTF-8 ${tempFile} > ${tempFileUTF8}
##### Issue Slack API
curl -H "Content-type: application/json" -H "Authorization: Bearer ${token}" -d @${tempFileUTF8} ${slackAPI} -x ${proxy} -k
echo;
##### Clean up
if [[ -e ${tempFile} ]]; then
rm ${tempFile}
fi
if [[ -e ${tempFileUTF8} ]]; then
rm ${tempFileUTF8}
fi
(2)files.uploadメソッドによるメッセージ送信スクリプト
参考: files.upload
#!/bin/sh
fileName=$1
channelName=$2
if [[ ! -e "${fileName}" ]]; then
echo "Error: file ${fileName} does noe exist."
exit 1
fi
if [[ -z ${channelName} ]]; then
channelName=general
fi
slackAPI=https://slack.com/api/files.upload
initialComment="'<!channel>' Notification from z/OS !"
token=xoxb-xxx
proxy=http://yyy:8080
tempDir=/tmp
tempKey=SlackFile
pid=$$
tempFileUTF8=${tempDir}/${tempKey}_${pid}_utf8.txt
##### Code Conversion
iconv -f IBM-1399 -t UTF-8 ${fileName} > ${tempFileUTF8}
##### Issue Slack API
curl -H "Authorization: Bearer ${token}" -F channels=${channelName} -F initial_comment="${initialComment}" -F file=@${tempFileUTF8} ${slackAPI} -x ${proxy} -k
echo;
##### Clean up
if [[ -e ${tempFileUTF8} ]]; then
rm ${tempFileUTF8}
fi
実行例
(1)chat.postMessage
スクリプト実行
$ cat test.txt
aaa
bbb
ccc
$ ./slack_chat.sh test.txt test01
{"ok":true,"channel":"CQLCGPXJA","ts":"1574243219.006200","message":{"type":"message","subtype":"bot_message","text":"<!channel> Notification from z\/OS !","ts":"1574243219.006200","username":"z\/OS bot","bot_id":"BQRA7JA4U","attachments":[{"text":"aaa \nbbb \nccc \n \n","id":1,"color":"2eb886","fallback":"aaa \nbbb \nccc \n \n"}]},"warning":"missing_charset","response_metadata":{"warnings":["missing_charset"]}}
(2)files.upload
スクリプト実行
$ cat test.txt
aaa
bbb
ccc
$ ./slack_file.sh test.txt test01
{"ok":true,"file":{"id":"FQRBBAMRS","created":1574243630,"timestamp":1574243630,"name":"SlackFile_83952320_utf8.txt","title":"SlackFile 83952320 utf8","mimetype":"text\/plain","filetype":"text","pretty_type":"\u30d7\u30ec\u30fc\u30f3\u30c6\u30ad\u30b9\u30c8","user":"UQRPSGM08","editable":true,"size":12,"mode":"snippet","is_external":false,"external_type":"","is_public":true,"public_url_shared":false,"display_as_bot":false,"username":"","url_private":"https:\/\/files.slack.com\/files-pri\/TQL21SB0X-FQRBBAMRS\/slackfile_83952320_utf8.txt","url_private_download":"https:\/\/files.slack.com\/files-pri\/TQL21SB0X-FQRBBAMRS\/download\/slackfile_83952320_utf8.txt","permalink":"https:\/\/tomotagwork.slack.com\/files\/UQRPSGM08\/FQRBBAMRS\/slackfile_83952320_utf8.txt","permalink_public":"https:\/\/slack-files.com\/TQL21SB0X-FQRBBAMRS-58fbe5385d","edit_link":"https:\/\/tomotagwork.slack.com\/files\/UQRPSGM08\/FQRBBAMRS\/slackfile_83952320_utf8.txt\/edit","preview":"aaa\nbbb\nccc\n","preview_highlight":"<div class=\"CodeMirror cm-s-default CodeMirrorServer\" oncopy=\"if(event.clipboardData){event.clipboardData.setData('text\/plain',window.getSelection().toString().replace(\/\\u200b\/g,''));event.preventDefault();event.stopPropagation();}\">\n<div class=\"CodeMirror-code\">\n<div><pre>aaa<\/pre><\/div>\n<div><pre>bbb<\/pre><\/div>\n<div><pre>ccc<\/pre><\/div>\n<div><pre><\/pre><\/div>\n<\/div>\n<\/div>\n","lines":4,"lines_more":0,"preview_is_truncated":false,"comments_count":0,"is_starred":false,"shares":{"public":{"CQLCGPXJA":[{"reply_users":[],"reply_users_count":0,"reply_count":0,"ts":"1574243630.007500","channel_name":"test01","team_id":"TQL21SB0X"}]}},"channels":["CQLCGPXJA"],"groups":[],"ims":[],"has_rich_preview":false}}
ファイルとしてデータが送付されているので、ダウンロードしてテキストファイルとして入手することができます。
※(1)のケースとアイコンが違うのは何故なんだろう?APIの出し方で違ってくるのかな?
補足
(1)のケースは、attachmentsという色々装飾ができる形式で本文中のメッセージとしてデータを送っています。color属性は"good"(Hexコードでの指定も可能)を固定で指定しているので緑色の縦棒がテキストの左側についていますが、Info/Warning/Errorなどのメッセージの種類に応じて色を制御する、みたいなこともできそうです。
curlコマンドにファイルでデータを渡す場合、UTF-8で渡す必要があるようです。渡すファイルはEBCDICの方が都合がよいと思うので、スクリプトの中でiconvによりUTF-8に変換するロジックを組み込んでいます(IBM-1399=>UTF-8変換)。
一応、DBCSを含むファイルを渡した場合も、きちんとSlack上に日本語として表示されていました。細かくテストできてませんが、slackに書き込むうえでエスケープが必要な特殊文字とか含まれていると、色々不都合あるかもしれません。
いずれも、送付先チャンネルの参加者全員に通知をするための@channel
メンションを埋め込んでいます。スクリプト中では<!channel>
と指定している部分です。
Slack連携の具体例
以前、Lightweight Command Utility on z/OS USSというツールを作りました。
参考: z/OSの新しい管理方法を探る - (4)Lightweight Command Utility on z/OS USS
これの拡張のイメージで今回Slack連携部品をUSS上のシェルスクリプトとして作成してますので、このユーティリティーを利用して、少し具体的なSlack連携例を実装してみたいと思います。
このユーティリティーで提供しているスクリプトを一部利用しているので、このユーティリティーが動くことが前提です。
また、上で作成したSlackメッセージ送信用のシェルスクリプトを配置しているディレクトリをPATHに追加しておく必要があります。
※Slackに送付されるデータに、セキュリティ上問題がある内容のものが含まれる可能性が無いかは充分注意しましょう!
SYSLOGの監視
一定間隔でSYSLOGをチェックし、事前に指定した文字列(特定のIDなど)が含まれるメッセージが出力されたらその列のみを抽出してSlackのメッセージとして送信します。
例えば、Spool使用率のメッセージを拾ってSlack経由で通知するとか、ロングランニングの処理の終了をSYSLOGメッセージから拾ってそれを通知するとか、その他特定のクリティカルなエラーが出た場合にそれを通知するとか、そんな利用方法をイメージしています。
ソース
ユーティリティで提供しているsyslog.shを元にして、カスタマイズしました(なのでちょっと余計なコード残っちゃってると思いますが)。内部的にはユーティリティで提供しているsyslog取得用のrex(syslog.rex)使ってます。
#!/bin/bash
scriptName=$(basename $0)
rexxFile=syslog.rex
######################################
# Function
######################################
. commonFunctions.sh
showHelp(){
echo "Usage: ${scriptName} [-h] | [-w [n] ] [-c <channel>] -f <fileName> "
echo " -h: Show this help"
echo " -w [n]: Watch(tail) syslog with specifed interval in second. n=1-300 (default: 2sec)"
echo " -c <channel>: Slack channel name (default general)"
echo " -f <fileName>: specify fileName including list of keyword to send to Slack"
echo " without option Display syslog of last 1 minute."
echo ""
exit 0
}
getRelativeTime(){
var=$1
if [[ ${thisMinute} -ge ${var} ]] ; then
min=$(expr ${thisMinute} - ${var})
if [[ ${min} -lt 10 ]] ; then
min=0${min}
fi
result=${thisHour}:${min}:${thisSecond}
else
if [[ ${thisHour} -eq 0 ]] ; then
result=00:00:00
else
hour=$(expr ${thisHour} - 1)
if [[ ${hour} -lt 10 ]]; then
hour=0${hour}
fi
min=$(expr 60 + ${thisMinute} - ${var})
if [[ ${min} -lt 10 ]] ; then
min=0${min}
fi
result=${hour}:${min}:${thisSecond}
fi
fi
echo ${result}
}
createGrepCommand(){
# arg1: fileName
fileName=$1
commandString="grep"
while read line
do
commandString="${commandString} -e ${line}"
done<${fileName}
echo ${commandString}
}
signalHandler(){
if [[ -e ${tempFile} ]]; then
rm ${tempFile}
fi
exit 1
}
#######################################
# Main Logic
#######################################
trap signalHandler 1 2 3 15
set -A arrayDateTime $(date '+%Y %m %d %H %M %S')
thisYear=${arrayDateTime[0]}
thisMonth=${arrayDateTime[1]}
thisDay=${arrayDateTime[2]}
thisHour=${arrayDateTime[3]}
thisMinute=${arrayDateTime[4]}
thisSecond=${arrayDateTime[5]}
#echo ${thisHour}:${thisMinute}:${thisSecond}
#fromRelativeTime=$(getRelativeTime 1)
#echo ${fromRelativeTime}
arg_w=2
arg_c=general
arg_f=
flag_w=1
flag_c=0
flag_f=0
arg_l=1
arg_fd=
arg_ft=
arg_td=
arg_tt=
flag_l=0
flag_fd=0
flag_ft=0
flag_td=0
flag_tt=0
for option in "$@"
do
case "$option" in
'-h')
showHelp
exit 0
;;
'-w')
flag_w=1
if [[ ${flag_fd} -eq 1 ]] || [[ ${flag_ft} -eq 1 ]] || [[ ${flag_td} -eq 1 ]] || [[ ${flag_tt} -eq 1 ]] ; then
echo "Error: -w option can not be specified with -fd, -ft, -td, -tt."
showHelp
exit 0
elif [[ -z "$2" ]] || [[ $(checkOption "$2") -ne 0 ]] ; then
# use arg_w default value
shift 1
elif [[ $(checkNumber "$2") -eq 0 ]] ; then
echo "Error: Argument of -w must be number"
showHelp
exit 0
elif [[ $2 -gt 300 ]] ; then
echo "Error: set 1-300 sec in -w option"
showHelp
exit 0
else
arg_w=$2
shift 2
fi
;;
'-c')
flag_c=1
if [[ -z "$2" ]] || [[ $(checkOption "$2") -ne 0 ]] ; then
echo "Error: Argument is required for -f"
showHelp
exit 1
else
arg_c=$2
shift 2
fi
;;
'-f')
flag_f=1
if [[ -z "$2" ]] || [[ $(checkOption "$2") -ne 0 ]] ; then
echo "Error: Argument is required for -f"
showHelp
exit 1
elif [[ ! -e "$2" ]]; then
echo "Error: file $2 does noe exist."
showHelp
exit 1
else
arg_f="$2"
shift 2
fi
;;
-*)
echo "Error: Unsupported option: $1"
showHelp
exit 0
;;
*)
if [[ ! -z "$1" ]] && [[ $(checkOption "$1") -eq 0 ]]; then
shift 1
fi
;;
esac
done
#echo arg_w: ${arg_w}
#echo arg_c: ${arg_c}
#echo arg_f: ${arg_f}
#echo arg_fd: ${arg_fd}
#echo arg_ft: ${arg_ft}
#echo arg_td: ${arg_td}
#echo arg_tt: ${arg_tt}
if [[ ${flag_f} -eq 0 ]]; then
echo "Error: -f option is required"
showHelp
exit 1
fi
grepCommand=$(createGrepCommand ${arg_f})
#echo ${grepCommand}
tempDir=/tmp
tempKey=SlackTest
pid=$$
tempFile=${tempDir}/${tempKey}_${pid}.txt
if [[ ${flag_w} -eq 1 ]]; then
fromDate=${toDate}
fromTime=${toTime}
sleep 1
while true
do
toDate=$(date '+%Y/%m/%d')
toTime=$(date '+%T')
${rexxFile} ${fromDate} ${fromTime}.01 ${toDate} ${toTime}.00 | ${grepCommand} > ${tempFile}
rc=$?
if [[ ${rc} -eq 0 ]]; then
timeStamp=$(date)
echo ${timeStamp} Send message to Slack - ${arg_c}
cat ${tempFile}
#slack_file.sh ${tempFile} ${arg_c}
slack_chat.sh ${tempFile} ${arg_c}
elif [[ ${rc} -eq 1 ]]; then
# no action
else
exit ${rc}
fi
sleep ${arg_w}
fromDate=${toDate}
fromTime=${toTime}
done
fi
exit
使い方
$ syslog2slack.sh -h
Usage: syslog2slack.sh [-h] | [-w [n] ] [-c <channel>] -f <fileName>
-h: Show this help
-w [n]: Watch(tail) syslog with specifed interval in second. n=1-300 (default: 2sec)
-c <channel>: Slack channel name (default general)
-f <fileName>: specify fileName including list of keyword to send to Slack
-f オプションで、ウォッチしたいキーワードのリストを含むファイル名を指定します <必須>
-c オプションで、送信したいチャンネル名を指定します (デフォルトはgeneral)
-w は、SYSLOGをチェックする間隔を秒単位で指定します (デフォルト 2sec)
実行例
監視対象のリストを準備(以下は、CICSの起動と停止のメッセージID)
DFHSI1517
DFHKE1799
スクリプト実行(無限ループするスクリプトなので、バックグラウンドで起動します)
[CICS004@EPLEX1:/u/cics004/Shell] syslog2slack.sh -c test01 -f slackMonitoringMessage.txt > output.log 2>&1 &
[1] 65884
上の例では、プロセスID: 65884でバックグラウンドでスクリプトが稼働し、2分間隔でSYSLOGをチェックするループが動いている状態となります。
ここで、CICSのリージョンを1つ起動してみます。(例: S CT54D4A1
)
すると、Slackに以下のメッセージが通知されました。
次に、このCICSリージョンを停止させてみます(例: F CT54D4A1,CEMT P SHUT
)。
すると、Slackに以下のメッセージが通知されました。
SYSLOG監視用スクリプトを終了させる場合は、先ほどのプロセスIDに対してkillコマンドでSIGTERMのシグナルを送付して停止させます。
[CICS004@EPLEX1:/u/cics004/Shell] kill 65884
[CICS004@EPLEX1:/u/cics004/Shell]
[1] + Done(1) syslog2slack.sh -c test01 -f slackMonitoringMessage.txt > output.log 2>&1 &
JOBLOG送付
ここでは、JCLをサブミットしたらそのJOBLOGを丸ごとファイルとしてSlackに送付するということをやります。
例えば、長時間かかるようなジョブを複数流して、あとからまとめてJOBLOG確認、みたいなケースをイメージしています。
ソース
この例では単純にJOBLOGまるごと送付してしまっていますが、手を加えればDDごとに分けたり指定したDDだけ出すというのもできると思います。JCLも手っ取り早くUSS上のファイルとして用意する前提ですが、ユーティリティで提供しているsumbmitJcl.shのように、MVS上のJCLを使用するようにカスタマイズも可能です。
内部的にはユーティリティで提供しているjoblog取得用のrex(joblog.rex)使ってます。
#!/bin/sh
jclFileName=$1
channelName=$2
tempDir=/tmp
tempKey=submit2slack
pid=$$
tempFile=${tempDir}/${tempKey}_${pid}.txt
data=$(submitJcl.sh -s -f ${jclFileName})
while read var1 var2
do
#echo "${var1} / ${var2}"
if [[ "${var1}" = "JobID:" ]]; then
JobID=${var2}
fi
done <<-END
${data}
END
echo JobID: ${JobID}
joblog.rex ${JobID} > ${tempFile}
slack_file.sh ${tempFile} ${channelName}
if [[ -e ${tempFile} ]]; then
rm ${tempFile}
fi
使い方
実行するJCLをUSS上のファイルとして用意し、第1引数にそのファイル名、第2引数に送信先のチャンネル名を指定します。
Syntax:
submit2slack.sh <jclFileName> <channelName>
実行例
実行したいJCLを用意(ここでは、USERCATALOGをリストするだけの単純なJCLを使用)
//LISTC JOB MSGCLASS=X,CLASS=A 00012038
//*----------------------------------------------------- 00013038
//* 00014038
//GO EXEC PGM=IDCAMS,REGION=0M 00018038
//SYSPRINT DD SYSOUT=* 00019038
//SYSIN DD * 00020038
LISTC USERCATALOG ALL 00021038
/* 00022038
このJCLを指定してスクリプトを実行
[CICS004@EPLEX1:/u/cics004/Shell] submit2slack.sh ../JCL/listc2.jcl test01
JobID: JOB06488
{"ok":true,"file":{"id":"FQYLGH9RU","created":1574476908,"timestamp":1574476908,"name":"SlackFile_33620071_utf8.txt","title":"SlackFile 33620071 utf8","mimetype":"text\/plain","filetype":"text","pretty_type":"\u30d7\u30ec\u30fc\u30f3\u30c6\u30ad\u30b9\u30c8","user":"UQRPSGM08","editable":true,"size":98103,"mode":"snippet","is_external":false,"external_type":"","is_public":true,"public_url_shared":false,"display_as_bot":false,"username":"","url_private":"https:\/\/files.slack.com\/files-pri\/TQL21SB0X-FQYLGH9RU\/slackfile_33620071_utf8.txt","url_private_download":"https:\/\/files.slack.com\/files-pri\/TQL21SB0X-FQYLGH9RU\/download\/slackfile_33620071_utf8.txt","permalink":"https:\/\/tomotagwork.slack.com\/files\/UQRPSGM08\/FQYLGH9RU\/slackfile_33620071_utf8.txt","permalink_public":"https:\/\/slack-files.com\/TQL21SB0X-FQYLGH9RU-4644981ba7","edit_link":"https:\/\/tomotagwork.slack.com\/files\/UQRPSGM08\/FQYLGH9RU\/slackfile_33620071_utf8.txt\/edit","preview":"isfmsg2.1 is: ISF767I Request completed.\nLISTC JOB06488 CICS004\n1 J E S 2 J O B L O G -- S Y S T E M Z O S 1 -- N O D E E P L E X \n0 \n 11.42.04 JOB06488 ---- SATURDAY, 23 NOV 2019 ----","preview_highlight":"<div class=\"CodeMirror cm-s-default CodeMirrorServer\" oncopy=\"if(event.clipboardData){event.clipboardData.setData('text\/plain',window.getSelection().toString().replace(\/\\u200b\/g,''));event.preventDefault();event.stopPropagation();}\">\n<div class=\"CodeMirror-code\">\n<div><pre>isfmsg2.1 is: ISF767I Request completed.<\/pre><\/div>\n<div><pre>LISTC JOB06488 CICS004<\/pre><\/div>\n<div><pre>1 J E S 2 J O B L O G -- S Y S T E M Z O S 1 -- N O D E E P L E X <\/pre><\/div>\n<div><pre>0 <\/pre><\/div>\n<div><pre> 11.42.04 JOB06488 ---- SATURDAY, 23 NOV 2019 ----<\/pre><\/div>\n<\/div>\n<\/div>\n","lines":2868,"lines_more":2863,"preview_is_truncated":true,"comments_count":0,"is_starred":false,"shares":{"public":{"CQLCGPXJA":[{"reply_users":[],"reply_users_count":0,"reply_count":0,"ts":"1574476908.000900","channel_name":"test01","team_id":"TQL21SB0X"}]}},"channels":["CQLCGPXJA"],"groups":[],"ims":[],"has_rich_preview":false}}
JOBLOGがファイルとして添付されているので、Slack上でインラインで確認もできますし、ファイルをダウンロードしてローカルのテキストエディター(Notepadやサクラエディタ)などで参照することができます。
おわりに
Slackで情報共有するってのは最近結構はやりなので、今回試してみました。
ここでは、USSのシェルスクリプトからcurlでAPI発行してますが、REST API発行できればよいので、他にも色々バリエーションは考えられます。冒頭のリンクにあったJavaしかり、その他では、例えばCICSは外部のWebサービスを呼び出すためのAPIを提供しているので、CICS-COBOLアプリからSlackにメッセージ出すこともできるはず。
z/OSの環境はネットワーク的に外部との接続がかなり制限されるので、なかなか実際の運用で活用するのは厳しいかもしれませんが、開発/テスト環境などで、通知や情報共有をSlackにつなげられると効率化図れる部分もあるかもしれません。
まぁ必ずしもz/OSから直接やらなくてもいいとは思いますけど、バリエーションの一つとして組み込める余地はありますよ、ということで。