Skip to content

Commit

Permalink
Merge pull request #66 from ibm-messaging/qstatus
Browse files Browse the repository at this point in the history
Qstatus
  • Loading branch information
parrobe authored Nov 20, 2018
2 parents 6eb2358 + b429947 commit 67a70ac
Show file tree
Hide file tree
Showing 11 changed files with 487 additions and 42 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Changelog

=======
### November 2018
* Added functions to mqmetric to issue DISPLAY QSTATUS for additional stats
* Added z/OS capability for minimal status

### October 2018
* Allow compilation against MQ v8

Expand Down
68 changes: 50 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,58 @@ This repository demonstrates how you can call IBM MQ from applications written i

> **NOTICE**: Please ensure that you use a dependency management tool such as [dep](https://github.com/golang/dep) or [Glide](http://glide.sh/), and add a specific version dependency.
This repository previously contained sample programs that exported MQ statistics to some monitoring packages. These have now been moved to a new [GitHub repository called mq-metric-samples](https://github.com/ibm-messaging/mq-metric-samples).
This repository previously contained sample programs that exported MQ statistics to
some monitoring packages. These have now been moved to a
new [GitHub repository called mq-metric-samples](https://github.com/ibm-messaging/mq-metric-samples).

A minimum level of MQ V8 is required to build these packages.
However, note that the monitoring data published by the queue manager is not available before MQ V9.
A minimum level of MQ V8 is required to build these packages. However, note that
the monitoring data published by the queue manager is not available before MQ V9.

## Health Warning

This package is provided as-is with no guarantees of support or updates. There are also no guarantees of compatibility with any future versions of the package; the API is subject to change based on any feedback.
This package is provided as-is with no guarantees of support or updates. There are
also no guarantees of compatibility with any future versions of the package; the API
is subject to change based on any feedback. Versioned releases are made in this repository
to assist with using stable APIs.

## MQI Description

The ibmmq directory contains a Go package, exposing an MQI-like interface.

The intention is to give an API that is more natural for Go programmers than the common procedural MQI. For example, fixed length string arrays from the C API such as MQCHAR48 are represented by the native Go string type. Conversion between these types is handled within the ibmmq package itself, removing the need for Go programmers to know about it.
The intention is to give an API that is more natural for Go programmers than the
common procedural MQI. For example, fixed length string arrays from the C API such
as MQCHAR48 are represented by the native Go string type. Conversion between these
types is handled within the ibmmq package itself, removing the need for Go programmers
to know about it.

A short program in the samples/mqitest directory gives an example of using this interface, to put and get messages and to subscribe to a topic.
A short program in the samples/mqitest directory gives an example of using this interface,
to put and get messages and to subscribe to a topic.

The mqmetric directory contains functions to help monitoring programs access MQ status and
statistics. This package is not needed for general application programs.

Feedback on the utility of this package, thoughts about whether it should be changed or extended are welcomed.

## Using the package

To use code in this repository, you will need to be able to build Go applications, and have a copy of MQ installed to build against. It uses cgo to access the MQI C structures and definitions. It assumes that MQ has been installed in the default location on a Linux platform (`/opt/mqm`) but you can easily change the cgo directives in the source files if necessary.
To use code in this repository, you will need to be able to build Go applications, and
have a copy of MQ installed to build against. It uses cgo to access the MQI C
structures and definitions. It assumes that MQ has been installed in the default
location (on a Linux platform this would be `/opt/mqm`) but this can be changed
with environment variables if necessary.

Some Windows capability is also included. This has been tested with Go 1.10 compiler, which now permits standard Windows paths (eg including spaces) so the CGO directives can point at the normal MQ install path.
Windows compatibility is also included. This has been tested with Go 1.10 compiler,
which now permits standard Windows paths (eg including spaces) so the CGO directives
can point at the normal MQ install path.

## Getting started

If you are unfamiliar with Go, the following steps can help create a working environment with source code in a suitable tree. Initial setup tends to be platform-specific, but subsequent steps are independent of the platform.
If you are unfamiliar with Go, the following steps can help create a working environment
with source code in a suitable tree. Initial setup tends to be platform-specific,
but subsequent steps are independent of the platform.

### Linux

* Install the Go runtime and compiler. On Linux, the packaging may vary but a typical directory for the code is `/usr/lib/golang`.
* Install the Go runtime and compiler. On Linux, the packaging may vary but a typical
directory for the code is `/usr/lib/golang`.

* Create a working directory. For example, ```mkdir $HOME/gowork```

Expand Down Expand Up @@ -81,6 +98,14 @@ set CC=x86_64-w64-mingw32-gcc.exe

`git clone https://github.com/ibm-messaging/mq-golang.git src/github.com/ibm-messaging/mq-golang`

* If you have not installed MQ libraries into the default location, then set environment variables
for the C compiler to recognise those directories. For example,

```
export CGO_CFLAGS="-I/my/mq/dir/inc"
export CGO_LDFLAGS="-I/my/mq/dir/lib64"
```

* Compile the `ibmmq` component:

`go install ./src/github.com/ibm-messaging/mq-golang/ibmmq`
Expand All @@ -89,9 +114,11 @@ set CC=x86_64-w64-mingw32-gcc.exe

`go install ./src/github.com/ibm-messaging/mq-golang/mqmetric`

* Follow the instructions in the [mq-metric-samples repository](https://github.com/ibm-messaging/mq-metric-samples) to compile the sample programs you are interested in.
* Sample programs can be compiled in this way

`go build -o bin/mqitest ./src/github.com/ibm-messaging/mq-golang/samples/mqitest/*.go`

At this point, you should have a compiled copy of the code in `$GOPATH/bin`.
At this point, you should have a compiled copy of the program in `$GOPATH/bin`.

## Limitations

Expand All @@ -111,11 +138,16 @@ See [CHANGELOG](CHANGELOG.md) in this directory.

## Issues and Contributions

For feedback and issues relating specifically to this package, please use the [GitHub issue tracker](https://github.com/ibm-messaging/mq-golang/issues).
Feedback on the utility of this package, thoughts about whether it should be changed
or extended are welcomed.

For feedback and issues relating specifically to this package, please use
the [GitHub issue tracker](https://github.com/ibm-messaging/mq-golang/issues).

Contributions to this package can be accepted under the terms of the IBM Contributor License Agreement,
found in the [CLA file](CLA.md) of this repository. When submitting a pull request, you must include a statement stating
you accept the terms in the CLA.
Contributions to this package can be accepted under the terms of the IBM Contributor
License Agreement, found in the [CLA file](CLA.md) of this repository. When
submitting a pull request, you must include a statement stating you accept the terms
in the CLA.

## Copyright

Expand Down
7 changes: 4 additions & 3 deletions ibmmq/mqi.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ the particular MQRC or MQCC values.
The build directives assume the default MQ installation path
which is in /opt/mqm (Linux) and c:\Program Files\IBM\MQ (Windows).
These would need to be changed in this file if you use a
non-default path.
If you use a non-default path for the installation, you can set
environment variables CGO_CFLAGS and CGO_LDFLAGS to reference those
directories.
*/
package ibmmq

/*
Copyright (c) IBM Corporation 2016
Copyright (c) IBM Corporation 2016, 2018
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
8 changes: 5 additions & 3 deletions ibmmq/mqiMQCNO.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package ibmmq

/*
Copyright (c) IBM Corporation 2016
Copyright (c) IBM Corporation 2016,2018
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -42,7 +42,7 @@ void setCCDTUrl(MQCNO *mqcno, PMQCHAR url, MQLONG length) {
mqcno->CCDTUrlOffset = 0;
mqcno->CCDTUrlPtr = NULL;
mqcno->CCDTUrlLength = length;
if (url != NULL) {
if (url != NULL && length > 0) {
mqcno->CCDTUrlPtr = url;
}
#else
Expand Down Expand Up @@ -181,7 +181,9 @@ func copyCNOtoC(mqcno *C.MQCNO, gocno *MQCNO) {
// The CCDT URL option was introduced in MQ V9. To compile against older
// versions of MQ, setting of it has been moved to a C function that can use
// the pre-processor to decide whether it's needed.
C.setCCDTUrl(mqcno, C.PMQCHAR(C.CString(gocno.CCDTUrl)), C.MQLONG(len(gocno.CCDTUrl)))
if gocno.CCDTUrl != "" {
C.setCCDTUrl(mqcno, C.PMQCHAR(C.CString(gocno.CCDTUrl)), C.MQLONG(len(gocno.CCDTUrl)))
}
return
}

Expand Down
11 changes: 10 additions & 1 deletion ibmmq/mqiPCF.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package ibmmq

/*
Copyright (c) IBM Corporation 2016
Copyright (c) IBM Corporation 2016,2018
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -252,6 +252,15 @@ func ReadPCFParameter(buf []byte) (*PCFParameter, int) {
case C.MQCFT_GROUP:
binary.Read(p, endian, &pcfParm.Parameter)
binary.Read(p, endian, &pcfParm.ParameterCount)

case C.MQCFT_BYTE_STRING:
// For now, the data is not actually stored anywhere as we don't need it
// But we do need to know how to step over the field
offset := int32(C.MQCFBS_STRUC_LENGTH_FIXED)
binary.Read(p, endian, &pcfParm.Parameter)
binary.Read(p, endian, &pcfParm.stringLength)
p.Next(int(pcfParm.strucLength - offset))

default:
fmt.Println("mqiPCF.go: Unknown PCF type ", pcfParm.Type)
// Skip the remains of this structure, assuming it really is
Expand Down
12 changes: 7 additions & 5 deletions mqmetric/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const (
)

var ChannelStatus StatusSet
var attrsInit = false
var chlAttrsInit = false
var channelsSeen map[string]bool

/*
Expand All @@ -65,7 +65,7 @@ text. The elements can be expanded later; just trying to give a starting point
for now.
*/
func ChannelInitAttributes() {
if attrsInit {
if chlAttrsInit {
return
}
ChannelStatus.Attributes = make(map[string]*StatusAttribute)
Expand Down Expand Up @@ -98,7 +98,7 @@ func ChannelInitAttributes() {
attr = ATTR_CHL_STATUS_SQUASH
ChannelStatus.Attributes[attr] = newStatusAttribute(attr, "Channel Status - Simplified", ibmmq.MQIACH_CHANNEL_STATUS)
ChannelStatus.Attributes[attr].squash = true
attrsInit = true
chlAttrsInit = true
}

// If we need to list the channels that match a pattern. Not needed for
Expand Down Expand Up @@ -199,6 +199,8 @@ func collectChannelStatus(pattern string, instanceType int32) error {
buf = make([]byte, 0)

cfh := ibmmq.NewMQCFH()
cfh.Version = ibmmq.MQCFH_VERSION_3
cfh.Type = ibmmq.MQCFT_COMMAND_XR

// Can allow all the other fields to default
cfh.Command = ibmmq.MQCMD_INQUIRE_CHANNEL_STATUS
Expand Down Expand Up @@ -252,7 +254,7 @@ func collectChannelStatus(pattern string, instanceType int32) error {
if cfh.Reason != ibmmq.MQRC_NONE {
continue
}
key := parseData(instanceType, cfh, replyBuf[offset:datalen])
key := parseChlData(instanceType, cfh, replyBuf[offset:datalen])
if key != "" {
channelsSeen[key] = true
}
Expand All @@ -263,7 +265,7 @@ func collectChannelStatus(pattern string, instanceType int32) error {
}

// Given a PCF response message, parse it to extract the desired statistics
func parseData(instanceType int32, cfh *ibmmq.MQCFH, buf []byte) string {
func parseChlData(instanceType int32, cfh *ibmmq.MQCFH, buf []byte) string {
var elem *ibmmq.PCFParameter

chlName := ""
Expand Down
21 changes: 18 additions & 3 deletions mqmetric/discover.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ var Metrics AllMetrics

var qList []string

func GetDiscoveredQueues() []string {
return qList
}

/*
DiscoverAndSubscribe does all the work of finding the
different resources available from a queue manager and
Expand Down Expand Up @@ -133,9 +137,9 @@ func discoverClasses(metaPrefix string) error {

// Have to know the starting point for the topic that tells about classes
if metaPrefix == "" {
rootTopic = "$SYS/MQ/INFO/QMGR/" + qMgr.Name + "/Monitor/METADATA/CLASSES"
rootTopic = "$SYS/MQ/INFO/QMGR/" + resolvedQMgrName + "/Monitor/METADATA/CLASSES"
} else {
rootTopic = metaPrefix + "/INFO/QMGR/" + qMgr.Name + "/Monitor/METADATA/CLASSES"
rootTopic = metaPrefix + "/INFO/QMGR/" + resolvedQMgrName + "/Monitor/METADATA/CLASSES"
}
sub, err = subscribe(rootTopic)
if err == nil {
Expand Down Expand Up @@ -292,6 +296,11 @@ func discoverStats(metaPrefix string) error {
// Start with an empty set of information about the available stats
Metrics.Classes = make(map[int]*MonClass)

// Allow us to proceed on z/OS even though it does not support pub/sub resources
if metaPrefix == "" && platform == ibmmq.MQPL_ZOS {
return nil
}

// Then get the list of CLASSES
err = discoverClasses(metaPrefix)

Expand Down Expand Up @@ -349,7 +358,7 @@ func discoverQueues(monitoredQueues string) error {
var err error
qList, err = inquireObjects(monitoredQueues, ibmmq.MQOT_Q)
if len(qList) > 0 {
fmt.Printf("Monitoring Queues: %v\n", qList)
//fmt.Printf("Monitoring Queues: %v\n", qList)
if err != nil {
// fmt.Printf("Queue Discovery: %v\n", err)
}
Expand Down Expand Up @@ -414,6 +423,8 @@ func inquireObjects(objectPatternsList string, objectType int32) ([]string, erro
putmqmd.Report = ibmmq.MQRO_PASS_DISCARD_AND_EXPIRY

cfh := ibmmq.NewMQCFH()
cfh.Version = ibmmq.MQCFH_VERSION_3
cfh.Type = ibmmq.MQCFT_COMMAND_XR

// Can allow all the other fields to default
cfh.Command = command
Expand Down Expand Up @@ -568,6 +579,10 @@ func ProcessPublications() error {
var elementidx int
var value int64

if platform == ibmmq.MQPL_ZOS {
return nil
}

// Keep reading all available messages until queue is empty. Don't
// do a GET-WAIT; just immediate removals.
cnt := 0
Expand Down
38 changes: 33 additions & 5 deletions mqmetric/mqif.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,17 @@ import (
"fmt"

"github.com/ibm-messaging/mq-golang/ibmmq"
"strings"
)

var (
qMgr ibmmq.MQQueueManager
cmdQObj ibmmq.MQObject
replyQObj ibmmq.MQObject
statusReplyQObj ibmmq.MQObject
getBuffer = make([]byte, 32768)
qMgr ibmmq.MQQueueManager
cmdQObj ibmmq.MQObject
replyQObj ibmmq.MQObject
statusReplyQObj ibmmq.MQObject
getBuffer = make([]byte, 32768)
platform int32
resolvedQMgrName string

qmgrConnected = false
queuesOpened = false
Expand Down Expand Up @@ -78,6 +81,31 @@ func InitConnection(qMgrName string, replyQ string, cc *ConnectionConfig) error
qmgrConnected = true
}

if err == nil {
mqod := ibmmq.NewMQOD()
openOptions := ibmmq.MQOO_INQUIRE + ibmmq.MQOO_FAIL_IF_QUIESCING

mqod.ObjectType = ibmmq.MQOT_Q_MGR
mqod.ObjectName = ""

qMgrObject, err := qMgr.Open(mqod, openOptions)

if err == nil {
selectors := []int32{ibmmq.MQCA_Q_MGR_NAME,
ibmmq.MQIA_PLATFORM}

intAttrs, charAttrs, err := qMgrObject.Inq(selectors, 1, 48)

if err == nil {
resolvedQMgrName = strings.TrimSpace(string(charAttrs[0:48]))
platform = intAttrs[0]
}
// Don't need the qMgrObject any more
qMgrObject.Close(0)

}
}

// MQOPEN of the COMMAND QUEUE
if err == nil {
mqod := ibmmq.NewMQOD()
Expand Down
Loading

0 comments on commit 67a70ac

Please sign in to comment.