Running User Commands in MO71

MO71 User Commands

The User Commands menu on a queue manager in MO71

Do you have a number of commands, or scripts, or batch jobs that you regularly use against your queue managers? How would like to be able to invoke them from the queue manager menu in MO71? In the latest version of MO71 just released, you can do just that. This might be useful when setting up MO71 for your operations team to use (see Delivering an MO71 Bundle to your MQ team) to pre-configure MO71 with the various scripts they should be using for tasks outside MO71.

MO71 User Command strmqm

A User Command that will invoke strmqm for the queue manager

Of course, not all user commands you might want to run would apply to all queue managers in your MO71 configuration, so when setting up User Commands in MO71 you can say which queue managers they apply to. For example, if your queue manager is local to MO71 you can use commands such as strmqm, but for remote queue manager’s you’ll need some sort of remote script to achieve the same. The easiest way to categorize your queue managers is to put them into various “networks” (MO71’s way of categorizing them – see Can you see your QMgr for the trees?). You could imagine a network called “Local” and another called “Remote” which you can then use to determine whether the User Command that runs strmqm can be used. You can make one User command definition which applies to all queue managers in the “Local” network and use the %q substitution character to pass the queue manager name through to the command.

Here you’ve seen an example of a substitution character allowing you to pull information from the location to build up the command. Other substitutions that pull information from the location details are as follows. If there are other things that might be useful to use from the location details as substitutions in User Commands, let us know.

Insert Meaning
%c Location CLNTCONN connection name
%g Location Group Name
%l Location Name
%q Queue Manager Name

If you need to use any environment variables, for example MQ_DATA_PATH or MQ_INSTALLATION_PATH, these can also be used in substitutions in your User Commands with the following syntax:-

%[MQ_DATA_PATH]
MO71 User Command SSH

A User Command to launch an SSH session to the machine the queue manager is on

For “Remote” queue managers you might want to have quick access to a telnet/putty/ssh session to the machine the queue manager is running on. The session you use might vary based on the platform of the machine. For example, for your z/OS queue managers you might want to start a 3270 session. So there’s another possible network, you could also categorize your queue managers by platform. You can use the %c substitution character to pass the connection name (without port number) through to the command.

These User Commands can run anything you can imagine doing in a script. These might be quite simple wrapper scripts or quite complex scripts. You could use it to run an MQSCX script that generates a report with a known file name, and then open the script with something like notepad to view the results.

mqscx -x -f -C "=import file(C:\MQGem\MQReport.mqx) parms(%1)"
notepad C:\MQGem\Output\MQReport_%1_%2_%3_%4.txt

The User Command would then run the command file:

C:\MQGem\runReport.cmd %q %y %m %d

When generating files, it is common to use dates and times in the file names. The other inserts available as substitutions in User Commands are those you can use to generate dates and time, as follows.

Insert Meaning
%d Day of the month e.g. 16
%D Day of the month e.g. Mon, Tue, Wed
%H Current time hours
%m Current Month e.g. Jan, Feb, Mar
%M Current time minutes
%S Current time seconds
%t Current time in HH.MM.SS format
%y Four digit year

You can also start other programs directly from MO71, and pass in the queue manager name as a parameter where required. For GUI applications make sure the User Command is defined with Hidden set to No or the GUI won’t be visible. If you are an MQSCX user as well as an MO71 user, you may prefer to use MQSCX rather than the MQSC window in MO71 for command line operations. You can create a user command to start an MQSCX session up for the queue manager through a User Command.

The flexibility of running any command scripts means you can do a lot with this, but if you think of other inserts or requirements on this feature, please drop us a line, or leave a comment below to let us know.


The new version can be downloaded from the MO71 Download Page. Any current licensed users of MO71 can run the new version on their existing licence. If you don’t have a licence and would like to try out MO71 then send an email to support@mqgem.com and a 1-month trial licence will be sent to you.

Scripts using foreach on the CCDT

foreachThere are different types of users who use MQSCX. Some like the interactive experience, with tab auto-complete of commands, keywords and objects names. Others like the ability to create and edit CCDT files suitable for any required version of IBM MQ. Others again like the powerful control language which makes writing scripts to interrogate your queue manager a breeze.

Freaking awesome, Paul! I wrote several reporting scripts for a customer a couple of months back. They declined to purchase MQSCX so I was forced to do much of the logic in the script, giving me I have a good basis for comparison of both approaches. The differential in lines of code, complexity and amount of additional billable time I spent would have paid for a site license for several years. The ROI is now more than doubled, possibly even 5x what it was.

User comment on MQSCX – see more at What our customers say

Sometimes those different use cases come together. The control language has a for loop concept where you can easily iterate over all the queue manager objects that are returned by the command server as the answer to an MQSC command, with a script something like this:-

@total = 0
foreach(DISPLAY QLOCAL(*) WHERE(CURDEPTH GT 0))
  @total = @total + CURDEPTH
endfor
print 'Total CURDEPTH =',@total

You can also write scripts that operate, not on queue manager objects, but on the contents of a CCDT file.

With the latest version of MQSCX, you can use the foreach construct on the items in your CCDT file in just same way as above. Here’s a small example:-

@ssl = 0;
foreach(DISPLAY CHANNEL(*) SSLCIPH)
  if (SSLCIPH)
    @ssl = @ssl + 1
  endif
endfor
print 'Found',@ssl,'SSL Channels out of',_numEach

Now with MQSCX V9.0.0 you can use the powerful control language to analyse and manipulate your CCDT files. Another example of using the foreach construct on a CCDT file can be see in Be sure of your CCDT Version


If you are a current MQSCX licence holder, you can simply download the new version of MQSCX and start using it. If you’re not a current licence holder, and you’d like to try out MQSCX, please email support@mqgem.com to request a trial licence.

Only run MQSC script on intended Queue Manager

Correct QMgr?IBM MQ is a robust product and queue managers can run without problems for months, or even years at a time. In fact, often when problems do occur they are the result of human error, and one example of such as error can be running an MQSC script against an unintended queue manager.

I think we have all pushed an MQSC file into the wrong queue manager. 😦

So, the question was asked, how can I ensure that I only run my MQSC script against the queue manager it was intended for?

This is something that MQSCX can help you with. There are a few ways you can do this.

Check that you are connected to the correct queue manager

One way to protect against running an MQSC script against the wrong queue manager is to start the script with a quick test to see which queue manager it is currently connected to and to exit the script immediately if it is found to be administering the wrong one.

if ( _qmgr != "QM1" )
  print "Wrong queue manager! Script expects to be used on QM1"
  leave
endif

_qmgr is an MQSCX system variable that tells you which queue manager you are administering. There are also some others that might be helpful when coding up checks at the start of a script, _connqmgr tells you which queue manager you are connected to, and will be different from _qmgr if you are using a via connection. _client tells you if you are connected as a client. You can see these in action in the DISPLAY DQM for Distributed blog post.

You could also make the check broader and have it query some attribute of the queue manager to determine if it was an appropriate queue manager to run the script against. For example, looking for the word “(Test)” in the queue manager description. This means a script couldn’t accidentally be run against one of your production queue managers. Using this simple technique, each script can be limited to a class of queue managers; Production, Test, Devt and so on.

DISPLAY QMGR DESCR
if (!findstr("(Test)",DESCR))
  print "Queue Manager",_qmgr,"does not appear to be a test QMgr"
  print "Queue Manager",_qmgr,"DESCR(",:n:DESCR,:n:")"
  leave
endif

Have the script connect to the appropriate queue manager

As an alternative, rather than having the script check that the human running it has used the correct queue manager, the script could make the connection itself.

=conn qm(QM1)
if (_lastrc)
  leave
endif
ALTER QMGR ...
DEFINE CHANNEL ...

A script with commands for different queue managers

Having made a script that connects to the correct queue manager before running the commands against it, it is not difficult to make the next step where you can have a script that issues commands on multiple queue managers – has a section for each queue manager prefixed by an =conn command.

=conn qm(QM1)
ALTER QMGR ...
ALTER CHANNEL ...
 
=conn qm(QM2)
ALTER QMGR ...
ALTER QLOCAL ...
 
=conn qm(Q3)
ALTER QLOCAL ...

This is only scratching the surface of what MQSCX can do, but it can certainly make administration of your IBM MQ queue managers less error prone.


If you’re not a current MQSCX licence holder, and you’d like to try out MQSCX, please email support@mqgem.com to request a trial licence.


I was prompted to write this post as a result of this list-server question.

Adding up

When answering this question I offered a suggestion that you could total the number of connections coming over a set of SVRCONN channels by using the following single line in MQSCX.

@total=0;foreach(DISPLAY CHSTATUS(*) CURSHCNV);@total=@total+CURSHCNV;endfor;print @total

This totaled all the CURSHCNV values for all the channels whose status was displayed.

It occurred to me that there are many other examples of commands where you might want to total up some integer attribute from a set of objects or status records.

  • DISPLAY QLOCAL(SYSTEM.ADMIN.*.EVENT) CURDEPTH
    How many event messages you have
  • DISPLAY CHSTATUS(*) WHERE(RQMNAME EQ MQG2) MSGS
    How many messages have been sent to queue manager MQG2
  • DISPLAY TPSTATUS(‘#’) TYPE(SUB) NUMMSGS
    How many messages have been sent to susbcribers
  • DISPLAY QLOCAL(*) OPPROCS
    How many putting applications are around

Instead of repeating the single line each time with changes to reflect the different command, it might be handy to have a little function that you could just throw a command at and it would do the totaling up for you. For example, you could do something like this:-

func totals(Command,Attribute)
  @total = 0
  foreach(@Command + ' ' + @Attribute)
    @total = @total + eval(@Attribute)
  endfor
  print 'Total of',@Attribute,'for all',@Command,'is',@total
endfunc

And then call it and get the result like so:-

totals("DISPLAY QLOCAL(Q*)","CURDEPTH")

Total of CURDEPTH for all DISPLAY QLOCAL(Q*) is 8

MQSCX functions are just so handy!

DISPLAY DQM for Distributed

If you’re familiar with the IBM MQ for z/OS product, you may have issued the DISPLAY DQM command which gives you the following output:

CSQX830I M901 CSQXRDQM Channel initiator active
CSQX831I M901 CSQXRDQM 8 adapter subtasks started, 8 requested 
CSQX832I M901 CSQXRDQM 5 dispatchers started, 5 requested
CSQX833I M901 CSQXRDQM 0 SSL server subtasks started, 0 requested
CSQX840I M901 CSQXRDQM 5 channels current, maximum 200 
CSQX841I M901 CSQXRDQM 4 channels active, maximum 200, including 0 paused 
CSQX842I M901 CSQXRDQM 0 channels starting, 1 stopped, 0 retrying
CSQX836I M901 CSQXRDQM Maximum channels - TCP/IP 200, LU 6.2 200 
CSQX845I M901 CSQXRDQM TCP/IP system name is TCPIP 
CSQX846I M901 CSQXRDQM TCP/IP listener INDISP=QMGR started, for port 1591 address *
CSQX849I M901 CSQXRDQM LU 6.2 listener INDISP=QMGR not started 

There is no equivalent command for a distributed queue manager, however, you can get most of the same information from various other commands. So we have created an MQSCX script to create an equivalent set of output for a distributed queue manager.

Clearly some of the information simply doesn’t apply to distributed queue managers; for example the number of adapter, dispatcher and SSL Server subtasks. Also the TCP/IP system name doesn’t really have an equivalent on a distributed platform – or at least certainly not one that can be retrieved from the command server. The equivalents of DISPLAY QMGR MAXCHL ACTCHL TCPCHL LU62CHL is to look in the queue manager’s qm.ini file for MaxChannels and MaxActiveChannels on distributed, which is something that cannot work unless you are running the script local to the queue manager.

So the script issues the various commands required to get the information and then prints out a set of equivalent looking lines to give you a similar output:

MQG1 Channel initiator active
MQG1 14 channels current, maximum 400
MQG1 12 channels active, maximum 400, including 0 paused
MQG1 0 channels starting, 1 stopped, 1 retrying
MQG1 TCP/IP listener TCP.LSTR started, for port 1701 address *
MQG1 LU6.2 listener not started

It starts by checking whether the Channel initiator is active, which for most people will be since it gets started automatically by the queue manager these days.

DISPLAY QMSTATUS CHINIT
if (CHINIT = "RUNNING")
  print _qmgr,'Channel initiator active'
endif

Then it looks in the qm.ini file (but only if you’re not connected by a client or via connection). MQSCX can read environment variables just as if they were user variables, so it can make use of the MQ environment variable MQ_DATA_PATH which is setup by setmqenv. The _client system variable is new in MQSCX V9.0.0.

if (!(_client | (_connqmgr != _qmgr)))
  @filename = @MQ_DATA_PATH+"/qmgrs/"+_qmgr+"/qm.ini"
  @hf = fopen(@filename,"r")
  if (@hf)
    while (fgets(@hf,@line) >= 0)

For each line in the qm.ini file it will check for MaxChannels and MaxActiveChannels.

if (findstri(@line, "MaxChannels") > 0)
  @offset = findstr(@line, "=")
  if (@offset > 0)
    @MaxChannelsStr = substr(@line,@offset+1,strlen(@line)-@offset)
    @MaxChannels = eval(@MaxChannelsStr)
  endif
endif

A simple foreach loop allows the script to total up the number of different channel states currently on show. This then allows the various channel status lines in the output to be printed.

Finally the script has another simple foreach loop for listeners which also makes use of the new _numEach system variable to detect if the loop has never been called.

foreach(DISPLAY LSSTATUS(*) ALL WHERE(TRPTYPE EQ TCP))
  print _qmgr, 'TCP/IP listener', LISTENER ,'started, for port', PORT, 'address', IPADDR
endfor
if (_numEach = 0)
  print _qmgr, 'TCP/IP listener not started'
endif

The complete function is available to download in our Example Scripts bundle.


If you’re not a current licence holder, and you’d like to try out MQSCX, please email support@mqgem.com to request a trial licence.

MQSCX functions

MQGem recently delivered a new version of MQSCX that supports the new IBM MQ V9 release. As well as support for the new command level, there were a number of other features in this new version of MQSCX. One of those new features was a popular customer request for the addition of functions to the MQSCX control language.

MQSCX FunctionsThere are some new examples available in our Example Scripts bundle, which demonstrate how to use functions. In this blog post we’re going to take a look at one of those samples, conns.mqx as a way to introduce you to functions. This function evolved from an earlier blog post, MaxChannels vs DIS QMSTATUS CONNS where it was used to demonstrate all the different ways application connections show up in a queue manager.

The first thing to notice comparing the script in the earlier blog post, to the one in the sample download, is that the code is topped and tailed by the following statements:-

func conns()
:
endfunc

This is how you define a function in the MQSCX control language. All the statements in between the func and endfunc statements make up the body of your function.

If you load up the sample into MQSCX by importing the file, and then show the contents of the function with the =show func(conns) list command, you’ll also see that the comments that immediately precede the function in the imported file are included in this display. A handy place to describe what your function does to remind you in the future.

MQSCX Extended MQSC Program – Version 9.0.0

Licenced to Paul Clarke

[16:58:24] =import file(C:\MQGem\MQSCX\conns.mqx)

[16:58:32] =show func(conns) list

***********************************************************************

* Function : conns *

* Purpose : Print out the current state of connections to the QM *

***********************************************************************

func conns()

 

=echo resp(no)

You can run the function simply by typing its name on the command line.

[16:58:32] conns

Total connections: 23

Local : 21

QMgr Chls : 0

Client Chls: 2

 

Total Running Channel instances: 1

QMgr Channels: 0

Client Channels: 1

MQG1>

The next comparison to make between this function and it’s original form in the earlier blog post, is use of the =echo statements. The intention of this script (in both cases) is that you don’t see all the commands and responses going to and from the command server, you just see the final totals printed on the screen. For the original script this meant at the start it switched off commands and responses, and then at the end, switched them back on again.

=echo cmds(NO)
=echo resp(NO)
:
=echo cmds(YES)
=echo resp(YES)

This would have the slightly undesirable effect that if you had either of those turned off before you ran the script, the script would inadvertently turn them back on again when it was done!

In the new version where the script is wrapped into function, there is only one =echo statement at the top.

=echo resp(NO)

There is no need to switch responses back on again because the =echo statement only applies inside the function. The function is a black box to any callers. It doesn’t impact any settings, such as these =echo settings, on the caller. Also notice that the default inside a function is that the commands are not shown anyway.

Functions aren’t just handy for wrapping your scripts into handy re-usable chunks, but also functions are very useful when they are parameterised. In this blog post we’re going to make some changes to the supplied conns() function to add a parameter. At the moment it totals all your connections. Now we’ll give it a parameter of an application name (or part of one) and it can total just those connections.

To indicate that a function has a parameter, the parameter name goes between the parentheses on the func conns() statement. When you later refer to that parameter in your script statements within the function you prefix it’s name with the ‘@’ symbol to show that it is a user variable.

func conns(ApplTag)
  :
  @ApplTag ...

In the MQSCX control language, parameters are not mandatory. We are still allowed to call the conns() function without giving it a parameter – if we code it that way. This would be a handy thing to have; without a parameter it totals everything, with a parameter it filters the totals by that application.

The way to achieve this – to have a parameter that can be optional – is to ensure that everywhere the parameter is used is preceded by a test to see if it exists. Now, I want to use the parameter more than once; first to check it against the APPLTAG that comes back from a DISPLAY CONN command, and then to check it against the RAPPLTAG that comes back from a DISPLAY CHSTATUS command. So rather than checking it exists multiple times, right up front I’ll test it and set it to a useful value if it doesn’t exist.

if (!exists(@ApplTag))
  @ApplTag = ""
endif

Now I can use the @ApplTag variable throughout the rest of the function with impunity.

In the old version of the script it just counted the totals that came back from a command issued to the command server – now it needs to look at the output to see whether the APPLTAG attribute contains the string supplied in the parameter. So let’s replace the initial two display commands with the following:-

@localconns = 0
@chlconns   = 0
foreach(DISPLAY CONN(*) CHANNEL APPLTAG)
  if (findstri(APPLTAG, @ApplTag))
    if (CHANNEL)
      @chlconns = @chlconns + 1
    else
      @localconns = @localconns + 1
    endif
  endif
endfor

Clearly if you ask to filter by an application name, none of the queue manager channel connections are going to match, but this code will still work when we don’t want to filter by anything because the findstri function returns TRUE when asked if the string contains the empty string.

N.B. findstri is the case insensitive version of findstr.

We also make a little tweak to the print out of the first line to reflect the use of the parameter by the user running the function.

And finally, the pre-existing foreach loop needs a little tweak. The DISPLAY CHSTATUS command is extended to also return the RAPPLTAG attribute, and then an if statement is added just as with the earlier for loop.

foreach(DISPLAY CHSTATUS(*) CURSHCNV CHLTYPE RAPPLTAG WHERE(STATUS EQ RUNNING)
  if (findstri(RAPPLTAG, @ApplTag))
    if (CHLTYPE = "SVRCONN")

Clearly you could take these changes even further, perhaps adding a parameter to configure a detail level for the output, with high detail showing all the channel names. The possibilities are endless. We hope you find functions a very useful addition to your MQSCX scripts.

Here’s the final view of the updated function with all the changes described in this post.

***********************************************************************
* Function : conns                                                    *
* Purpose  : Print out the current state of connections to the QM     *
***********************************************************************
func conns(ApplTag)

  if (_connqmgr = '')
    print 'Please connect to your queue manager before issuing "conns"'
    return
  endif

  if (!exists(@ApplTag))
    @ApplTag = ""
  endif

  @localconns = 0
  @chlconns   = 0
  foreach(DISPLAY CONN(*) CHANNEL APPLTAG)
    if (findstri(APPLTAG, @ApplTag))
      if (CHANNEL)
        @chlconns = @chlconns + 1
      else
        @localconns = @localconns + 1
      endif
    endif
  endfor

  if (@ApplTag)
    print 'Total connections containing "',:n:@ApplTag,:n:'":',@localconns + @chlconns
  else
    print 'Total connections:',@localconns + @chlconns
  endif
  print '   Local      :',@localconns

  @total = 0
  @svrcn = 0
  foreach(DISPLAY CHSTATUS(*) CURSHCNV CHLTYPE RAPPLTAG WHERE(STATUS EQ RUNNING)
    if (findstri(RAPPLTAG, @ApplTag))
      if (CHLTYPE = "SVRCONN")
        @svrcn = @svrcn + 1
        @total = @total + CURSHCNV
      endif
    endif
  endfor
  print '   QMgr Chls  :',@chlconns-@total
  print '   Client Chls:',@total
  print
  print 'Total Running Channel instances:',_matches
  print '   QMgr   Channels:',_matches - @svrcn
  print '   Client Channels:',@svrcn

endfunc

If you are a current MQSCX licence holder, you can simply download the new version of MQSCX and start using it. If you’re not a current licence holder, and you’d like to try out MQSCX, please email support@mqgem.com to request a trial licence.

MQSCX version 9.0.0 is released

MQGem Software is pleased to announce that a new version of MQSCX, our command line extended MQSC tool for IBM MQ, is now available.

The main features of the release are as follows:-

Support for MQ Command Level 900

As normal with a new release of IBM MQ, there is a new command level. MQSCX now supports this new command level and its contents.

foreach

foreach(…) loop now operates in CCDT mode

Previous releases of MQSCX allowed the use of the foreach(…) statement to process each response from the command server. This release extends that processing to work with the responses to commands issued against the CCDT. For more information see Scripts using foreach on the CCDT

New iteration system variables loops

New system variables _idxEach, _idxItem, _idxWhile, _numEach, _numItem and _numWhile which can make processing loops easier.

Support of CCDT URL

IBM MQ V9 allows a connecting application to specify the URL location of the CCDT file to use. This field can now be specified on the =conn command. For more information see Using the CCDT URL.
MQSCX Functions

Support for functions

You can now define lists of commands which can be invoked from the command line, other functions or expressions. For more information see MQSCX functions, which has a worked example.

Support for GOTO

You can now jump to labelled parts of your code.
MQSCX Bootstrap

Automatically loads bootstrap.mqx file

This can be useful to load useful functions so they are always available. Although it could equally be used to always run a specific command or several commands every time the MQSCX tool is started. For more information see MQSCX Bootstrap file.

Various improvements to the usability of the debugger

Including commands to support the new functions capability, such as sf to set your current stack frame.

New eval() function

This function allows the user to create more dynamic expressions by having the contents of strings evaluated as an expression. For example, print eval(“curdepth > 0”).


The new version can be downloaded from the MQSCX Download Page. Any current licensed users of MQSCX can run the new version on their existing licence. If you don’t have a licence and would like to try out MQSCX then send a note to support@mqgem.com and a 1-month trial licence will be sent to you.