Category Archives: Avaya Stuff You Should Know

Python scripting tool to manage Avaya Communication Manager

This could be it!

So many of us hunger for a decent tool to automate getting useful information out of the Avaya Communication Manager! I have written many scripts using PHP and Perl, but they all have various issues.

It looks like an engineer and python programmer has created a script that uses OSSI, which is a low-level protocol to talk to the CMs. He probably had to do a lot of brilliant hacking to get this to work. I wish I had an Avaya to play with! Alas, my world is all Cisco now (which also has surprisingly difficult challenges with command-line automation).

Anyway, I wanted to pass along the link to this person’s project. I saw it on LinkedIn here: https://www.linkedin.com/feed/update/urn:li:activity:6648167619008483328

And here is the project itself: https://pypi.org/project/ossi-tool/

As I post this, we are four weeks into the COVID-19 “shelter in place” order. Many of us telephone engineers have been involved in setting up remote workers and probably dealing with capacity and congestion. This Python tool should help with that. I can imagine setting up a script that:

  1. Watches for a new “mobility” flag in Active Directory
  2. If set, pulls the extension and mobile number from Active Directory
  3. Launches a script to build the EC500 record in CM with the right mobile number
  4. Lists all EC500 and watches for changes between AD and CM
  5. Removes the EC500 mapping when the flag is cleared

Also, we could all use more visibility into trunk utilization. This Python script should allow us to monitor trunk usage as well.

Happy Scripting All! Please let me know how it works for you. And send feedback and thanks to https://www.linkedin.com/in/janos-tarjanyi-a4201226/ for this awesome tool!

Roger

 

SigMa Cookbook (for Avaya SBCE / Sipera SBCs)

Hello Everyone! I’m excited to say I got my hands on an unofficial “SigMa Cookbook” from a buddy of mine at Avaya. This was put together by some engineers at Sipera. This was passed around within Avaya and maybe some customers, but I don’t think it was officially released.

These recipes were put together in 2010 and 2011 for “generic release 4.0.4 Q102 or later”. I do not think there’s official support for this. It was an internal document and was always sent with various warnings about “your mileage will vary”. So you have been warned! If you find any errors and clean up some of these, please let me know and I’ll fix them here.

I have formatted it as HTML for easy searching. I do not know if the engineers want me to include their names. If any of you are from Avaya or Sipera and want attribution for these recipes, please let me know and I’m happy to do it!

And also let me know if you have any good examples we should add. There’s not much of a user community for these SBCs so let’s help each other!

Example 1: P-Asserted-Identity (PAI) Header Manipulation

Use Case:

The P-Asserted-Identity header field can be used to present the identity of the originator of a request within a trusted network. Since the “From” header field is populated by the originating User-Agent it may not contain the actual identity. It usually is established by means of authentication between the originating User-Agent and its outgoing proxy. The outgoing proxy then adds a P-Asserted-Identity header field to assert the identity of the originator to other proxies.

  1. If there is no P-Asserted-Identity header field present, a proxy MAY add one containing at most one SIP or SIPS URI, and at most one tel URL.
  2. If the proxy received the message from an element that it does NOT trust and if there is a P-Asserted-Identity header present, the proxy MUST replace the SIP URI or remove it.

Script:

within session "ALL"  {
    /* 
        Looks into all the messages 
        Message should be a request (act on request) and the messages coming towards the Uc-Sec should be considered,
        i.e. the destination of the message should be Uc-Sec ({0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}DIRECTION="INBOUND").
        The actions are invoked as soon as the message comes from the wire ({0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}ENTRY_POINT="AFTER_NETWORK") 
    */

    act on request where {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}DIRECTION="INBOUND" and {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}ENTRY_POINT="AFTER_NETWORK" {
        /*
            Checks if the first P-Asserted-Identity header is present/exists in the message. 
            Each header is represented as {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["<Header-name>"][<Header position>].
            For headers such as From and Contact, the Header Position is always 1.
            For headers like Via and P-Asserted-Identity, the positions can range from 1 to n
        */

        if(exists({0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["p-asserted-identity"][1])) then {
            remove({0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["p-asserted-identity"][1]); //Remove the header
        } else {
            /*
                If the P-Asserted-Identity header is not found in the message
                Add a SIP and a telephone URI.
            */
            {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["p-asserted-identity"][1] = "12345<sip:12345@192.168.150.150>";
            {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["p-asserted-identity"][2] = "tel:+14085554000";
        }
    }
}

Additional Description:

The script looks into each message that comes in (since the script acts on all sessions) and checks if

  1. It is a request message
  2. The message is inbound towards the Uc-Sec

If both the above conditions are successful, as soon as the message comes from the wire, (and after the basic sanity checks and DoS checks are performed on the message), the action is performed.  The script checks to see if a “P-Asserted-Identity” header exists.  If it does, it removes it, else it adds the header.

Limitations: 

If you would like to remove all the P-Asserted-Identity headers, it is assumed that you know the maximum number of headers that would be present in the messages.  You do not need to know the exact number of headers that come in because if you perform an operation on a header that does not exist, it is simply ignored.

Note:

If {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS[“<Header-Name>”][<Header Position>] is already present, then the operation

{0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["<Header-Name>"][<Header Position>] = <Val>

will modify the header. If it is not present in the message,

{0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["<Header-Name>"][<Header Position>] = <Val>

will add the header to the message.

Example 2: Addition of a media attribute in SDP

Use Case:

It might be required to add/modify SDP attributes or connection parameters for interoperability.

Script:

within session "INVITE" {
    /*
        Looks into messages in the INVITE session only (It includes all messages in the INVITE dialog)
    */
    
    act on request where {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}DIRECTION="INBOUND" and {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}ENTRY_POINT="AFTER_NETWORK" {
        /*
            The "m=" field in SDP contains information about the type of media session. 
            It includes the format-list parameter for specifying the codecs.
            Assuming that the message comes in with 2 codecs, we add a third codec as 101
        */

        {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}SDP[1]["s"]["m"][1].FORMATS[3]="101";    

        /*
            The "a=" field contains attributes to provide more information on the codecs.
            Assuming that the message does not have any fmtp attribute, we add the first one as 101 0-16
        */

        {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}SDP[1]["s"]["m"][1].ATTRIBUTES["fmtp"][1]="101 0-16";    
    }
}

Additional Description:

The script looks into all the messages of the “INVITE” session.  A session is defined as a SIP dialog and has the same lifetime as that of a dialog.  A new format-type and an attribute is added corresponding to fmtp.

Limitations:

We would need to know the number of codecs (number of formats in format-list parameter  and attributes), or we would end up replacing an existing format-type.

Example 3: Changing Privacy Header Value

Use Case:

Restricting Calling Party Presentation.

Script:

within session "ALL" {
    act on message where {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}DIRECTION="INBOUND" and {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}ENTRY_POINT="AFTER_NETWORK" {
        /*
            Checks if the privacy header value matches with the regular expression given (e.g. "none").
            If it matches, then the privacy header value is changed to "id"
        */

        if({0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["Privacy"][1] = "none") then {
            {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["Privacy"][1] = "id";
        }
    }
}

Example 4 – Replace “From” header for a set of users

Use Case:

In an organization, there could be several phones used by the employees and each of them might have a unique “From URI” associated with them.  It may be required that all calls going out from the organization have the same “From URI”.  For this purpose, the following script can be used.

Script:

within session "INVITE" {     
    /* 
        For users whose Uri begins with the prefix 10, when the message comes towards the Uc-Sec, 
        the Uri is changed to “9000”<sip:9000@domain>. So, when the receiver answers the call, 
        the From is 9000.
    */

    act on request where {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}DIRECTION="INBOUND" and {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}ENTRY_POINT="AFTER_NETWORK" {
        /*
            A Uri can be represented as "<diplay_name>"<scheme>:<user>@<host>:<port>, 
            eg: "roger"<sip:roger@rogerthephoneguy.com:5060>. 
            URI.USER extracts the user portion of the URI.  
            regex_match tries to match the string against the regular expression. 
            It is of the form <string>.regex_match("<regular expression>"). In this example,
            it is checked if the USER portion in the "From" Header starts with the prefix 10 
        */

        if({0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["From"][1].URI.USER.regex_match("^10")) then {
            /*
                The uri and display name of the actual user is stored in temporary variables
            */

            {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}OriginalFromUri = {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["From"][1].URI.USER;
            {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}OriginalFromName = {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["From"][1].DISPLAY_NAME;        
    
            /* 
                The display name and uri is changed to the new values.
            */

            {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["From"][1].DISPLAY_NAME = "9000";
            {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["From"][1].URI.USER = "9000";          
        }
    }

    /*
        When the response comes back, we need to change the URI USER and DISPLAY NAME to the actual user. 
        So,before the message is sent out to the wire from the Uc-Sec, it is checked if the URI.USER is 9000.
        If yes, then change it back to the original user's details. 

        Message should be a response (act on response) and the messages going out 
        from the Uc-Sec should be considered ({0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}DIRECTION="INBOUND"). The actions are 
        invoked before the message goes out ({0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}ENTRY_POINT="BEFORE_NETWORK") 
    */

    act on response where {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}DIRECTION="OUTBOUND" and {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}ENTRY_POINT="BEFORE_NETWORK" {
        /*
            Check if the user portion of the From URI is 9000
        */

        if({0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["From"][1].URI.USER = "9000") then {
            /*
                Change the URI.USER and display name to the original user’s details, 
                which are saved in the temporary variables
            */

            {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["From"][1].URI.USER = {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}OriginalFromUri;
            {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["From"][1].DISPLAY_NAME = {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}OriginalFromName;
        }
    }
}

Additional Description:

The above example shows how to modify a message (request) on its way out and also modify a message (response) when it comes in.

Limitations:

The example illustrates the use of regex_match.  The regular expression provided within the parentheses, i.e. regex_match(<regular expression>), can be any valid Perl regular expression.  However, the “$” symbol (which typically anchors the match to the end of the pattern) cannot be used in the regular expression.

Example 5: Editing the “Allow” Header

Use Case:

The Allow header is used to indicate the methods supported by the user agent, e.g. “Allow: INVITE, ACK, BYE, INFO, OPTIONS, CANCEL”.  The OPTIONS method is used to query a user agent or server about its capabilities and discover its current availability. The response to the request lists the capabilities of the user agent or server.  This may not be desired (probably due to security reasons).  In this case, the Uc-Sec can strip the OPTIONS method from the Allow header before sending out the message.

Script:

within session "INVITE" {

    /*
        Look for INVITE messages only. This is specified with the extra condition 
        {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}METHOD="INVITE" in the where clause
    */

    act on request where {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}DIRECTION="INBOUND" and {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}ENTRY_POINT="AFTER_NETWORK" and {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}METHOD="INVITE" { 

        /*
            There could be (1) multiple methods in Allow or (2) OPTIONS could be the only method in Allow. 
            If there are multiple methods in Allow, OPTIONS could be (1) in 
            the beginning (2) in the middle, or (3) the end 
        */

        /*
            If OPTIONS is in the middle/end in Allow, it would be of the form 
            Allow:<Methods>,OPTIONS,<More methods> or Allow:<Methods>,OPTIONS. 
            So, we try to match Allow against the regex ",OPTIONS"
        */

        /*
            Note from RogerThePhoneGuy - this example had ,OPTIONS in the comment
            but comma [space] OPTIONS in the regex below. You might want to check
            traces to see if there's a space between the comma and the word OPTIONS
            and make sure you match it in this script!
        */

        if({0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["Allow"][1].regex_match(",OPTIONS")) then {
            
            /*
                <string1>regex_replace("<regex1>","<string2>") looks for 
                regex1(regular expression) in string1 and replaces it with 
                string2(plain string). Here we replace ",OPTIONS" with
                an empty string, indirectly removing ",OPTIONS"
            */

            {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["Allow"][1].regex_replace(",OPTIONS",""); 
            /* 
                Note from Roger: The original example had a space between the comma and the word OPTIONS
                but I think that is incorrect so I removed it for this example.
                I do not have an SBC at the moment though - please check your traces!
            /*
        } else {
            /*
                Nested if-else
            */

            /*
                If OPTIONS is in the beginning in Allow, it would be of the form 
                Allow: OPTIONS,<More methods>. So, we try to match Allow 
                against the regex "OPTIONS,"
            */

            if({0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["Allow"][1].regex_match(" OPTIONS,")) then {
                /*
                    We replace OPTIONS, with an empty string, indirectly removing " OPTIONS,"
                */

                {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["Allow"][1].regex_replace(" OPTIONS,", "");
            } else {
                /*
                    If OPTIONS is the only method in Allow, it would be of the form 
                    Allow: OPTIONS. So, we try to match Allow against the regex " OPTIONS"
                */

                if({0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["Allow"][1].regex_match(" OPTIONS")) then {

                    /*
                        Since OPTIONS is the only method in Allow, we remove the entire header
                    */

                    /*
                        remove({0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["<Header-name>"][<Posn>] removes the header specified in 
                        <Header-name> in Position <Posn>. Here we remove the Allow header
                    */

                    remove({0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["Allow"][1]);
                }
            }
        }
    } 
}

Additional Description:

The above script can come in handy while operating on headers such as Allow, Supported, Content-Type etc, whose values can’t be extracted individually as compared to headers like “From”, “To”, or “Contact”.

Limitations:

The regular expression in regex_replace can’t have the $ symbol.

Example 6: Prefix Stripping

Use Case:

Phone numbers may come in with a prefix. Sometimes this needs to be stripped off before the call is routed. This is useful in scenarios where a call transfer is made and the number to which the call has to be transferred is entered with a prefix.

Script:

within session "INVITE" {
    /*
        Look for REFER messages only. This is specified with the extra 
        condition {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}METHOD="REFER" in the where clause
    */

    act on request where {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}DIRECTION="INBOUND" and {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}ENTRY_POINT="AFTER_NETWORK" and {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}METHOD="REFER" {

        /*
            The User portion of the URI in the Refer-To header is checked to 
            see if it starts with the prefix 011. If it does, then it is 
            replaced with an empty string. If URI.USER does not 
            match the regex, then the action is ignored and the message is left intact.
        */

        {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["Refer-To"][1].URI.USER.regex_replace("^011","");
    }
}

Additional Description:

The messages which have the “Refer-To” method are checked if the URI contains a prefix.  If so, it is stripped before sending it out.

Limitations:

The regular expression in regex_replace can’t have the $ symbol.

Example 7: Diversion Header Addition and phone context removal

Use Case:

The user may want the option to add a diversion header or manipulate the extension field or change the URI parameters in the header. This example also illustrates combining two functions such as addition of a diversion header only to the INVITE and the removal of phone-context from all outbound messages.

Script:

within session "ALL" //Looks into all the messages {
	act on message where {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}DIRECTION="OUTBOUND" and {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}ENTRY_POINT="POST_ROUTING" {

		/* 
			Any message that is outbound from the UC-Sec is checked 
		*/

		/*
			Remove phone-context from Request Line
		*/

		remove({0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["Request_Line"][1].PARAMS["phone-context"]);

		/*
			Remove phone-context from the From header
		*/

		remove({0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["From"][1].PARAMS["phone-context"]);

		/*
			Remove phone-context from the To header
		*/

		remove({0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["To"][1].PARAMS["phone-context"]); 

		if ({0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}INITIAL_REQUEST = "true") then {

			/*
				Add a diversion header
			*/

			{0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["Diversion"][1] = "sip:3145552375@rogerthephoneguy.com";

		}
	}
}

Example 8: Redirection

Use Case:

The user may require to populate the  From header in the outbound INVITE message with the contents of the Diversion header in the incoming INVITE.

Script:

within session "ALL" {
	act on message where {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}DIRECTION="OUTBOUND" and {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}ENTRY_POINT="POST_ROUTING" and {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}METHOD="INVITE" {

		/*
			Store the contents of diversion header URI
		*/

		{0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}Div = {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["Diversion"][1].URI;

		/*
			Replace the contents of From header with stored value
		*/

		{0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["From"][1].URI  = {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}Div;
	}
}

Example 9: Invite XML Body Manipulation

Use Case:

The user may require manipulation of the contents of the xml body present in the INVITE message.

Script:

within session "INVITE" {     
    act on request where {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}DIRECTION="OUTBOUND" and {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}ENTRY_POINT="POST_ROUTING" {

    	/*
    		Check the body for the regex value 
    	*/

        if({0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}BODY[1].regex_match("sip:2911@192.168.3.150")) then {
        	/* 
        		Replace the matching string with sip:anonymous@anonymous.com 
        	*/   
            {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}BODY[1].regex_replace("sip:2911@192.168.3.150","sip:anonymous@anonymous.com");   
        }
    }
}

Example 10: Switching From and To tags

Use Case:

The user may require to switch tags on the From and To in a particular message. The following example illustrates tag switching in the 200 OK message.

Script:

within session "REGISTER" {
    act on response where {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}DIRECTION="INBOUND" and {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}ENTRY_POINT="AFTER_NETWORK" and {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}RESP_CODE="200" {
        {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}from = {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["From"][1].PARAMS["tag"];
        {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["From"][1].PARAMS["tag"] = {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["To"][1].PARAMS["tag"];
        {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}HEADERS["To"][1].PARAMS["tag"] = {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}from;
    }
}

Note from Roger: Does anyone know why you’d want to swap the from and to? This was the 10th example in the doc but I cannot think of why this would be useful.

How to translate four digits to an eleven digit PSTN call in Avaya Communication Manager

Hello all!

I was recently contacted by an old client who is moving offices. They have four-digit dialing between offices and use a lot of h.323 tie lines. However, they’re migrating to Cisco and during the transition, they just want to convert four digits to a PSTN call. Most of our telephone systems probably do this, but we rarely need to change it or add new ones, so I figured I’d summarize the process here.

Typically, four-digit translation is done based upon the first two digits (i.e. 100 numbers at-a-time). There are really only a few steps: Create the entry in the “dialplan analysis”, then the “uniform-dialplan”, then the “aar digit-conversion”. It is possible to perform some digit-conversion in the uniform dialplan, but I prefer to use the aar digit-conversion table due to consistency and flexibility. In this example, we add translation for the 83xx range to dial 914-555-83xx.

Step 1 – Verify the Dialplan is available for the first two digits

There should no entry for ‘83’ in the “dialplan analysis” table.

display dialplan analysis                                       Page   2 of  12
                             DIAL PLAN ANALYSIS TABLE
                                   Location: all            Percent Full: 11

    Dialed   Total  Call     Dialed   Total  Call     Dialed   Total  Call
    String   Length Type     String   Length Type     String   Length Type
   8           4   ext
   80          3   dac
   86          4   ext
   87          4   ext

Step 2 – change the dialplan analysis

Next, change the dialplan analysis adding an ‘83’ with a total length of 4 and a call-type of ext. We’re really going to use AAR to route this call, but that will be done through the uniform dialplan next. Confirm the entry:

display dialplan analysis                                       Page   2 of  12
                             DIAL PLAN ANALYSIS TABLE
                                   Location: all            Percent Full: 11

    Dialed   Total  Call     Dialed   Total  Call     Dialed   Total  Call
    String   Length Type     String   Length Type     String   Length Type
   8           4   ext
   80          3   dac
   83          4   ext
   86          4   ext
   87          4   ext

Step 3 – Check uniform dialplan

Next, check the uniform dialplan. It is possible to have entries in the uniform-dialplan that are not in the dialplan analysis table (as far as I can tell, this is a mistake because you’ll get wave-off). In my case, I don’t have 83s:

list uniform-dialplan                                                  Page   8

                      UNIFORM DIAL PLAN TABLE

Matching Pattern   Len   Del   Insert Digits   Net    Conv   Node Num

7977                4     4      7577          ext     n
7978                4     4      7578          ext     n
7983                4     4      7583          ext     n
7986                4     4      7586          ext     n
7988                4     4      6111          ext     n
8651                4     4      1111          ext     n
8662                4     4      1111          ext     n

Step 4 – OPTION 1 – Uniform dialplan only

Like everything else in a telephone system, there are multiple ways to do this. Let’s call this OPTION 1 where we only use the Uniform Dialplan (and not aar analysis)

Create an entry in the uniform dialplan for 83 with a length of 4 and no deleted digits. However, we are going to insert the digits here to route through the PSTN. In this case, insert 1914304. We set the Network to ‘ars’ so that this call goes through ARS routing. Note that we do not include a ‘9’ in the inserted digits. This is because we’re explicitly telling the PBX to use ARS routing (which is also what the ‘9’ does). Also note that you can only insert 10 digits here, which is not enough for some international translations. In that case, I use ars with digit translation. But this is fine for USA numbers. However, to be consistent between a mix of USA and international, I will typically use ARS digit-conversion as described in the next step OPTION 2.

list uniform-dialplan                                                  Page   8

                      UNIFORM DIAL PLAN TABLE

Matching Pattern   Len   Del   Insert Digits   Net    Conv   Node Num

7977                4     4      7577          ext     n
7978                4     4      7578          ext     n
7983                4     4      7583          ext     n
7986                4     4      7586          ext     n
7988                4     4      6111          ext     n
83                  4     0      1917304       ars     n
8651                4     4      1111          ext     n

[Note, the option above works for the North American numbering plan, but not for longer international strings, so I always use the next option]

Step 4 – OPTION 2 – AAR digit conversion

This is the most consistent (and my preferred) option, because if you have any translations to international numbers you’ll probably need to use this method. Create an entry in the uniform dialplan for 83 with a length of 4 and no deleted digits. However, we also want to perform digit conversion on this pattern so be sure to stroke the “Conv” column to ‘y’ so additional conversion can take place.

list uniform-dialplan                                                  Page   8

                      UNIFORM DIAL PLAN TABLE

Matching Pattern   Len   Del   Insert Digits   Net    Conv   Node Num

7977                4     4      7577          ext     n
7978                4     4      7578          ext     n
7983                4     4      7583          ext     n
7986                4     4      7586          ext     n
7988                4     4      6111          ext     n
83                  4     0                    ars     y
8651                4     4      1111          ext     n

Next, create an entry in the aar digit-conversion table for 83. Set the min/max to 4 and insert the new digits in the replacement string. Then set the Net to “ars” to tell the PBX to route the call out to the PSTN. There will be no more digit conversion, so leave Conv to ‘n’.

list aar digit-conversion

                      AAR DIGIT CONVERSION REPORT

                                Location: all

Matching Pattern     Min   Max   Del   Replacement String   Net  Conv ANI Req

0                    1     28    0                          ars   y
1                    4     28    0                          ars   y
5982                 4     4     4     12155550542          ars   n
7317                 4     4     0     555929               aar   y
7501                 4     4     4     13105555425          ars   n
83                   4     4     0     1917555              ars   n
x11                  3     3     0                          ars   y

And that’s it! You should be able to dial 83xx and it will use ARS routing to the translated value of 191755583xx. If you have any trouble at all, you can refer to my flowchart of Avaya routing to help troubleshoot this.

Happy routing, everyone!

Roger

 

How to Parse Avaya Communication Manager data to a database for analysis – Part 3

Hello all!

This is a continuation of our discussion about pushing Avaya Communication Manager data into a database so you can analyze the results.

Here is part 1 where we connect to the Communication Manager through a perl script

Here is part 2 where we capture a bunch of data and parse it into useful tables

In this part, we push the data into a MySql database. The first step is to create the database. I typically use my own Linux workstation. You can ask your VM team for one. They take almost no resources and they’re open-source, so there are no licensing concerns. Therefore I typically have complete control of the repositories, database, and Apache instance. If you do not have the root password to your Linux workstation, then you might need help from your team with some of these steps.

We need to start with some assumptions here. So let’s assume you have followed parts 1 and 2 of this series and you have MySQL installed, okay?

Step 1 – create the database

Log into MySQL. These commands will create the database and user for this project. This user is called ‘scraper’ with a password of ‘crafty1876’

mysql> create database avaya;
Query OK, 1 row affected (0.01 sec)

mysql> use avaya;
Database changed

mysql>create user scraper@localhost identified by 'crafty1876';
Query OK, 0 rows affected (0.04 sec)

mysql>grant all on avaya.* to scraper@localhost;
Query OK, 0 rows affected (0.00 sec)

mysql>exit

We exit the database and log back in with our new user. This ensures that the user works and has full access to the avaya database

[roger ~]# mysql -hlocalhost -uscraper -pcrafty1876 avaya

Now let’s create the tables we need. I always have a “drop table if exists” statement so I can tweak the table as much as I want and re-create it. This creates a keyvalues table and a display name table.

mysql> drop table if exists avaya_keyvalues;
Query OK, 0 rows affected, 1 warning (0.06 sec)

mysql]> create table avaya_keyvalues (
 id integer auto_increment primary key,
 active integer,
 key_name varchar(256),
 key_value varchar(256),
 record_status integer,
 created_on datetime,
 updated_on datetime,
 deactivated_on datetime
);
Query OK, 0 rows affected (0.11 sec)

mysql>drop table if exists avaya_display_name;
Query OK, 0 rows affected, 1 warning (0.06 sec)

mysql>create table avaya_display_name (
 station varchar(12) NOT NULL,
 name varchar(32),
 PRIMARY KEY (station)
);
Query OK, 0 rows affected (0.09 sec)

mysql>

Now we have a database and the empty tables we need. Since you have read part 1 and part 2 of this process, you should have a perl script generating your ‘keyvalue.txt’ file.

Step 2 – Push the keys and values into the database

We have a new perl script that takes the file of keys and values and puts it into the database. You can click here for a download the script. Just rename it to .pl. Here it is so you can see it. It is called keys2db.pl:

#!/usr/bin/perl

#provided as-is by in 2017 by RogerThePhoneGuy.com 
#This script reads a file of "key/value" pairs, and logs them to a database. If the value was changed or deleted, it logs that too
#meant to be used with keyval.php, which shows this data
#assumes there's a local mysql connection

use strict;
use DBI;

my $db;

sub db_open { 
	my $dsn = "DBI:mysql:avaya:localhost";
	my $user = "scraper";
	my $pass = "crafty1876";
	while (!($db)) {
		$db = DBI->connect($dsn, $user, $pass);
		sleep 1;
	}
}

sub db_close { 
	if($db) {
		$db->disconnect();
		sleep 1;
	}
	undef($db);
}

sub generic_replace { 
	my ($table, $fields, $values) = (@_);
	my $sql = "replace into $table ($fields) values ($values)";
	my $sth = $db->prepare($sql);
	if(!($sth->execute())) {
		print "WARNING - unable to replace with $sql\n";
	}
	$sth->finish();
}

sub generic_create { 
	my ($table, $fields, $values) = (@_);
	my $sql = "insert into $table ($fields) values ($values)";
	my $sth = $db->prepare($sql);
	if(!($sth->execute())) {
		print "WARNING - unable to insert with $sql\n";
	}
	$sth->finish();
}

sub generic_read {
	my ($select, $table, $where) = (@_);
	my @datarow;
	my $sql = "select $select from $table where $where limit 1";
	my $sth = $db->prepare($sql);
	if($sth->execute()) {
		@datarow = $sth->fetchrow_array();
	} else {
		print "unable to execute $sql\n";
	}
	$sth->finish();
	return $datarow[0];
}

sub generic_update {
	my ($table, $set, $where) = (@_);
	my $sql = "update $table set $set where $where";
	my $sth = $db->prepare($sql);
	if(!($sth->execute())) {
		print "WARNING - unable to update with $sql";
	}
	$sth->finish();
}

sub generic_delete {
	my ($table, $where) = (@_);
	my $sql = "delete from $table where $where";
	my $sth = $db->prepare($sql);
	if(!($sth->execute())) {
		print "WARNING - unable to update with $sql";
	}
	$sth->finish();
}

db_open();

my $counter = 0;
generic_update("avaya_keyvalues","record_status=9","active=1");
open SCRAPED, $ARGV[0];
while (my $line = ) {
	chomp($line);

	my ($key,$value) = split(/\t/,$line);
	my $id = generic_read("id", "avaya_keyvalues","active=1 and key_name='$key'") + 0;
	my $currvalue = generic_read("key_value", "avaya_keyvalues","active=1 and key_name='$key'");
	if($id > 0) {
		if($value ne $currvalue) {
			$value =~ s/'/\\'/g;
			generic_update("avaya_keyvalues","active=0,record_status=2,updated_on=now()","id=$id");
			my $fields = "id,active,key_name,key_value,record_status,created_on,updated_on,deactivated_on";
			my $values = "null,1,'$key','$value',1,now(),now(),null";
			generic_create("avaya_keyvalues", $fields, $values);	
		} else {
			generic_update("avaya_keyvalues","active=1,record_status=1","id=$id");
		}
	} else {
		$value =~ s/'/\\'/g;
		my $fields = "id,active,key_name,key_value,record_status,created_on,updated_on,deactivated_on";
		my $values = "null,1,'$key','$value',1,now(),now(),null";
		generic_create("avaya_keyvalues", $fields, $values);
	}
	if($key =~ /\.station\.(\d+)\.status/) {
		my $station = $1;
		my $name = '';
		if($value =~ /^(.+?) \(ip=/) {
			$name = $1;
		} elsif($value =~ /^(.+) \(unregistered/) {
			$name = $1;
		}
		$name =~ s/'/\\'/g;
		generic_replace("avaya_display_name","station,name","'$station','$name'");
	}
}
close SCRAPED;
generic_update("avaya_keyvalues","active=0,record_status=3,deactivated_on=now()","record_status=9");

my $create = "
drop table if exists avaya_keyvalues;
create table avaya_keyvalues (
	id integer auto_increment primary key,
	active integer,
	key_name varchar(256),
	key_value varchar(256),
	record_status integer,
	created_on datetime,
	updated_on datetime,
	deactivated_on datetime
);
";

#my @count = generic_read("count(*)","callrecord","1=1");
#print "count is " . $count[0] . "\n";
db_close();

This script is really basic. It just does these things:

  1. Line 88, It marks all records in the database as ‘old’
  2. Starting in line 90, It cycles through the keyvalues file and checks for records where the value has changed (line 97). It marks these as ‘changed’ and then inserts the new record. It marks the unchanged records as ‘unchanged’ (line 104).
  3. If the current value is not in the database, it inserts it (line 110)
  4. It also replaces the value of the display name in case we want to see all extensions and names later (line 121)
  5. Lastly, any record that was not touched through this loop is NOT in the text file. Does that make sense? So any record that is still marked ‘old’ in step 1 must have been deleted from the Avaya system. So we mark these records as ‘deleted’ (line 125).

When this script is done, we have a list of all active records in the database. We also have a list of old values and deleted values. This gives you a rolling history of all extensions in your database. I cannot tell you how handy it is to see a history of an extension. You can see when the IP address changes, when registration changes, when firmware changes, network region, etc. And of course when a station is deleted you still have a record of it. This is very handy.

For those of you interested in Perl, there are also a bunch of helpful database functions in here. I keep these in my back pocket and often copy/paste them to the next project.

To tie this new perl script into our ‘sanity.sh’ script from last time, we should now have this:

file=/home/roger/avaya/sanity/data/sanitycheck_`date +"{0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}Y-{0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}m-{0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}d-{0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}H-{0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}M"`.txt
perl /home/roger/avaya/sanity/av.pl /home/ARES/randerson/avaya/sanity/la.pbx /home/roger/avaya/sanity/commands.txt >$file
perl /home/roger/avaya/sanity/sanity.pl $file > /home/roger/avaya/sanity/keyvalues.txt
perl /home/roger/avaya/sanity/keys2db.pl /home/roger/avaya/sanity/keyvalues.txt

At the end of this shell script, you should have a fresh copy of all stations in your PBX in the database! Ready for reading with a simple PHP script. That will be next time, and hopefully MUCH sooner than this post. Sorry for the delay.

Before I finish, I want to thank you for reading this. I recently “moved” from an Avaya environment to a Cisco environment, so I don’t have easy access to the Avaya environment anymore. I am using some copies of the scripts so if you find any syntax errors or something doesn’t work, please let me know and we’ll get it working.

Once I finish this series, I’ll show you a way to AUTOMATICALLY generate labels for the 96xx phones. That’s the most exciting thing I’ve been wanting to share with you!

Roger

 

How to parse Avaya Communication Manager output into useful csv or database tables

Hello all!

This is a follow-up to my post about extracting data from Avaya Communication Manager. In that post, I showed you how to extract any “list”, “display”, or “status” from Avaya Communication Manager to a text file. First, I would like to show you my bash shell script that extracts this data. And then I’ll show you my perl script that converts the output to a text file that you can open with Excel or import into a database.

What information should you pull from Communication Manager? Well, in my case, I have a text file called ‘commands.txt’ that contains these lines:

display time
list ips
list surv
list med
status cdr
status trunk 1
status trunk 2
status trunk 8
status trunk 11
status trunk 16
status trunk 21
status trunk 79
list station
list reg
status station 2291
status station 2292
status station 2293
status station 2294
list call-forwarding
list off s
display time

So those are the various pieces of information that I care about. Looking at that info, those are my active trunk groups, and I want to know the status of four particular extensions (those happen to be the digital ports of my fax server). Also, I have a port network, so I want the status of IPSIs. Anyway, think about all the stuff you care about when you log into your PBX in the morning. And stick them in that “commands.txt” file. I put a “display time” at the beginning and end so I can look at the file later and tell how long it took to run the file.

So we want to run these commands automatically. So let’s create a file called “sanity.sh” and put this in the file:

file=/home/roger/avaya/sanity/data/sanitycheck_`date +"{0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}Y-{0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}m-{0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}d-{0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}H-{0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}M"`.txt
perl /home/roger/avaya/sanity/av.pl /home/ARES/randerson/avaya/sanity/la.pbx /home/roger/avaya/sanity/commands.txt >$file
perl /home/roger/avaya/sanity/sanity.pl $file

Once you save the file, make it executable with

chmod +x sanity.sh

Let’s go over this script line-by-line, okay?

  1. The first line creates a variable for the file that will contain the data. There’s a little linux trick to embed a timestamp in the filename.
  2. The second line runs the file av.pl that I shared with you in a previous post about this script. It’s magical. I cannot take all the credit, but I have modified it for our purposes.
  3. The third line runs a file that parses the output of all the commands. This is a very useful script and I can take full credit for this one. It generates a list of “key/value” pairs to a text file.

My thought process with this script is to create a unique key for every piece of useful data. For example, I want the name, IP address, firmware version, gatekeeper, and network region for every station. I also want the EC500 mapping. And I would like the port number for analog stations. Oh! I also want a sense of low or high priority (Is it bad if the data changes?) and I need to include a site identifier in case I have the same extension in multiple sites. That’s a lot of information to display. And, if I have 1000 stations, it’s too much to display all this information on separate lines. That’s like 7000 lines of data for my stations. Fine for machines to read, but it seemed like too much for humans. I want to put them into one (and in the case of ec500, two) lines of data. I do this by assigning a “Key name” for my stations like this:

Key Name Key Value
lo.la.station.8348.offpbx EC500=>2135552978, OPS=>8348
lo.la.station.8348.status Anderson, Roger (ip=10.10.60.138;reg=3;ver=3.260A;gk=10.2.86.180)

See how much data I squeezed into that? And note that very few stations will have off-pbx information. Most stations will just have that station.status key. The script called sanity.pl takes the raw output from the Communication Manager and converts it to these key/value pairs above. Here is a link to sanity.pl. You’ll need to rename it to a ‘.pl’ file. This should compile just fine. When you copy it to your linux server, try

perl -c sanity.pl

That -c just means to check the syntax and don’t run it. It hopefully says “syntax okay”. Actually, I’m sorry but you’ll need to edit the file and change line 45 to the path where you want your ‘keyvalues.txt’ file, which will contain all your data. This should use a command line param, but it doesn’t. Sorry.

You might consider looking in more detail at this file. It’s where the magic happens and it’s a good framework for parsing other data. If you’re interested, here is a summary of how it works:

  • Starting in line 56, we check the raw text file line-by-line looking for the header for each ‘list’ or ‘display’ or ‘status’ section. For example, when we see the text ‘REGISTERED IP STATIONS’, then we know we are expecting to see the result of a ‘list reg’ (by setting our variable of type to 7).
  • Then on line 183 we actually process the registration status of stations. If type=7, then we are in the ‘list reg’ mode and if a line contains registration information for a station, then we capture the data.
  • Each section has some customization, but you’ll notice that (almost) each section performs a ‘set_data’ of the keys and values appropriate for that section.
  • At the end, the trunk group and station summary is generated and the keys are reported alphabetically.

Here are some examples of what the script does:

  • The script parses ‘list station’ and assumes all stations are unregistered.
  • The script parses ‘list reg’ and fills in the registration status for all stations (anything not parsed in this section has a status of ‘unregistered’)
  • The script parses ‘status trunk xxx’ and simply counts the trunks that are in service (ignoring the “idle/in use” values. We just count trunk members in service
  • The script parses ‘status cdr’ and simply stores the percent full of the buffers
  • The script parses ‘list off s’ and stores the off pbx mappings for all stations
  • The script parses various critical fields for IPSIs, media gateways, and survivable servers
  • The script parses ‘list call-forwarding’ and generates a list of all stations that are call forwarded.

At this point, if you ran the bash script, you would have an awesome list of keys and values in a text file called keyvalues.txt. You probably don’t need to know anything about Perl to get this working for you. But if you do know a bit of Perl, you’ll be able to do amazing things with it.

There’s more!

This data is useless unless you’re looking at it. In a future post, I will show you how to schedule this script with cron, push the data into a database, read it with a simple ‘web site in a single file’ php script, filter the data (including historical values!), and get alerts when the values change.

And then I have something REALLY amazing to show you!

Thanks for reading, everyone! Feel free to contact me at roger@rogerthephoneguy.com or post a comment here. If you need help getting this code working, or you want to tweak it a bit, let me know!

Roger

How to automatically extract data from an Avaya Communication Manager

You might call this post “the secret of my success” as a PBX admin for Avaya Communication Manager. Avaya provides some utilities for their techs and business partners, but customers don’t really have a lot of tools to easily extract data. In this post, I’ll show you how to extract ANYTHING from Avaya from a command line so it’s suitable for scripting. In my case, I parse the data further and generate a list of all stations and report of changes to my PBX every 15 minutes. Have you ever wondered who used to have extension 4438? How about that phone for the intern over the summer named “Bodkin Van Horn” that you deleted but now he has been hired and you want to give him the same extension? How about the question “how long has this extension been unplugged”?

Back in mid 00s, I wrote some PHP integrate with CM using telnet. Since I didn’t know much about PHP’s ANSI support, I ended up writing my own ANSI parser of the raw data. Anyway, it was great, but many customers don’t support telnet and want all communication to CM to be through ssh, which is reasonable, right?

Then I found this post from Benjamin Roy where he provided a perl module to do it through ssh. Also, he meticulously reverse-engineered Avaya’s OSSI protocol, which I think is impressive and sounds like fun. But I didn’t use OSSI; I just wanted a way to get data using ssh. So I took Ben’s code and tweaked it a little bit. It’s straight perl code – it’s not a module or anything like that. It does have dependencies on a couple other modules though. You do not need to be a perl developer to use this. It’s really easy. Here is a link to the script. Just save this as “av.pl”. Try to run it with “perl -c av.pl” (the -c means just check to make sure it compiles but do not run it). You might get some errors about missing modules. There are only three, and this is how you install them if you need to:

perl -MCPAN -e "install Expect"
perl -MCPAN -e "install Data::Dumper"
perl -MCPAN -e "install Term::VT102"

I should probably mention here – if you don’t have a Linux server in your environment, you should really get one. Your Virtual Machine team can make one for you – just about any distribution of Lunux will do and they take almost no resources. If you need to justify it, just say something like “utility server for PBX monitoring” or something like that.

Anyway, back to the script. Naturally, it needs to connect to your PBX, so create a little text file like “mypbx.txt” with the contents

10.10.40.89,5022,cmusername,Passw0rd,ssh

Obviously, replace the IP address, username, and password of your PBX.

And then create another file called “commands.txt” with the contents

list trunk
display station 3100

This can contain whatever commands you’d like. list reg, list station, list trunk, list locations, list measurements, stat cdr, etc. It should be any command that can be “paged” through, you know?

And then simply run this line

perl av.pl mypbx.txt commands.txt

The script will connect to the PBX defined in mypbx.txt, run the commands in commands.txt and output the results to STDOUT!

This is the core script to a bunch of stuff I automate in my PBXs. In a later post, I’ll show you how I parse the results with Perl and make use of the data.

I call this “the secret of my success” because I use this data to build a simple html page of all extensions in all of my PBXs every 15 minutes. I keep this page up all the time in a browser tab, which enables me to simply “control-f” for any name in the PBX and I have the extension number and the IP address of the station (or ‘unregistered’ if it’s unplugged). I can see unregistered ‘guest’ numbers, see the station’s history, etc. There’s a lot more I do with this information and I look forward to showing you.

Please let me know what you think! And if there’s anything I can do to help you get it working.

Lastly, if any of you need to do any telephone system testing (capacity, QA, DID number ports, call-flow, queuing, etc), please check out my post about CallsByCloud. If you sign up and use the promotion code ‘roger2015’, you’ll get $10 in credit and a 20{0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0} discount on the per-minute rate.

Also, if you hate unsolicited outbound telemarketing, consider subscribing to the Jolly Roger Telephone Company. Your telemarketers will entertain and delight you. Sample recordings here.

Thanks all!

Roger

 

Avaya Call Detail Recording to Kiwi syslog server

In a previous post about busy/release of CDR, I asked if anyone was curious about capturing CDR from Avaya Communication Manager using a Kiwi Syslog server. At least one of you was, so here it is.

There are two parts of course. One part is programming the phone system to send CDR. And the other part is programming Kiwi to capture CDR. These can be done in either order, but if you program the phone system first, the CDR buffer will fill as is tries to establish the connection with Syslog.

So let’s start with Kiwi. Your version may differ. In my case, the network team already had a rather powerful installation of Kiwi on some great hardware. All I had to do was configure it to capture Avaya CDR. So when you launch the Kiwi dashboard, click File->Setup and in the “Server Setup” window, scroll to the bottom for “Inputs”.

kiwi-config-0

In the TCP section, tell Kiwi to listen for TCP messages on whatever port you wish. I’m using 5013 – probably because I saw it in an Avaya doc somewhere. But you can use any port.

Then scroll up to the “Rules” section and configure a new rule to match for messages from your Avaya Communication Manager.

kiwi-config-1

In my case, I named my rule “Call Records from LA PBX”. I set a filter by IP address. If you also send any other information to Kiwi from the CMs, then you’ll want to filter by the Priority field. You want the Local7 field to be Debug. I don’t recall why this works. I think I was looking at the data one day to try to separate the CDR from the other syslog messages from Avaya and this seemed the easiest way to do it.

kiwi-config-3

Once you have those two filters – IP address and Local7.Debug, you need to create an action. My action above is a little misleading. All you really need is one that logs to a file, then stops processing. If you’d like, you can also display it to one of the virtual displays in Kiwi. I log to a text file on the local file system:

kiwi-config-2

Now, if we program our PBX correctly, we should get CDR to these text files!

Step 2 – Program the Avaya Communication Manager

Programming the Communication Manager is three steps: Add the node-name, set the ip-services, and change the system-parameters cdr.

To set the node name, ‘change node-name ip’ and create an entry for the IP address of your Kiwi server. This is basically the DNS lookup for the CM. This sets the name and IP address. Any name will do. Enter the IP address of your Kiwi server.

change node-names ip                                            Page   1 of   2
                                  IP NODE NAMES
    Name              IP Address
SL_LSP              10.10.60.10
CHA_LSP             10.10.1.10
Chicago             10.10.148.2
Chicago_LSP         10.30.10.10
DO_LSP              10.20.16.10
Smoot               10.87.176.11
Gateway006          10.46.12.1
LA                  10.10.19.6
LA_ESS              10.44.192.10
LE_ESS              10.58.200.10
LA_SM               10.59.29.16
( 16 of 44   administered node-names were displayed )
Use 'list node-names' command to see all the administered node-names
Use 'change node-names ip xxx' to change a node-name 'xxx' or add a node-name

Next, set the ip-services. I have squeezed all three screens here. The important thing here is page three. We need to set the “Reliable Protocol” to n. I think Avaya defaults this to y and it works great for their pre-packaged CDR partners. For home-grown syslog, we set this to n. It may not hold up in court if there’s ever a dispute.

change ip-services                                              Page   1 of   3

                                   IP SERVICES
 Service     Enabled     Local        Local       Remote      Remote
  Type                   Node         Port        Node        Port
CDR1                 procr            0       la_syslog       5013
CDR2                 procr            0       prognosis       50000

change ip-services                                              Page   2 of   3

                                   IP SERVICES
 Service     Enabled     Local        Local       Remote      Remote
  Type                   Node         Port        Node        Port


change ip-services                                              Page   3 of   3

                              SESSION LAYER TIMERS
  Service     Reliable  Packet Resp   Session Connect  SPDU  Connectivity
   Type       Protocol     Timer       Message Cntr    Cntr     Timer

 CDR1            n         10                3          3         10
 CDR2            n         10                3          3         10


And last step – set the system-parameters for CDR. Here’s the thing. You have lots of options here. You can define fields, separators, etc. But I discovered that there is a line length limit that is not obvious. Also, the caller-id name is surprisingly absent. Rather than customize the CDR, I have gone back to “unformatted” and let further processing figure it out.

change system-parameters cdr                                    Page   1 of   1
                            CDR SYSTEM PARAMETERS

 Node Number (Local PBX ID): 2                     CDR Date Format: month/day
      Primary Output Format: unformatted   Primary Output Endpoint: CDR1
    Secondary Output Format: unformatted Secondary Output Endpoint: CDR2
           Use ISDN Layouts? n                   Enable CDR Storage on Disk? n
       Use Enhanced Formats? n      Condition Code 'T' For Redirected Calls? n
      Use Legacy CDR Formats? y                 Remove # From Called Number? n
Modified Circuit ID Display? n                             Intra-switch CDR? y
                  Record Outgoing Calls Only? n     Outg Trk Call Splitting? y
  Suppress CDR for Ineffective Call Attempts? n       Outg Attd Call Record? y
      Disconnect Information in Place of FRL? y      Interworking Feat-flag? n
 Force Entry of Acct Code for Calls Marked on Toll Analysis Form? n
                                    Calls to Hunt Group - Record: member-ext
Record Called Vector Directory Number Instead of Group or Member? n

     Inc Trk Call Splitting? n
  Record Non-Call-Assoc TSC? n           Call Record Handling Option: warning
      Record Call-Assoc TSC? n   Digits to Record for Outgoing Calls: dialed
   Privacy - Digits to Hide: 0               CDR Account Code Length: 15
Remove '+' from SIP Numbers? y


I have a perl script that parses unformatted cdr. Anyone want to see it?

UPDATE 2021-09-15 – I posted the Perl script to process unformatted Avaya CDR here.

Anyway, when you have this set up, you can status CDR to make sure the link is up:

status cdr-link
                                CDR LINK STATUS
                   Primary                      Secondary

       Link State: up                           up

      Date & Time: 2016/04/17 08:43:29          2016/05/09 10:12:20
  Forward Seq. No: 0                            0
 Backward Seq. No: 0                            0
CDR Buffer Full:   0.00                         0.00
      Reason Code: OK                           OK





The good news here is the link state(s) are “up”  and the CDR buffer full is 0. If you have trouble, you can “busy cdr primary” and “release cdr primary” to close and open the socket. You should see a blip in the Kiwi server when this happens.

Make a call and watch Kiwi! You may need to check the log files rather than rely on the Kiwi display. The extra CR/LF will sometimes make Kiwi look funky but the text file is usually just fine.

Let me know how it goes for you!

Roger

 

Avaya CM – will I lose the CDR buffer if I busy/release the cdr link?

I have an Avaya Communication Manager sending CDR to a kiwi syslog server. For some reason, the link is down and the buffer is filling up. What happens if I busy/release the CDR link? Will I lose the buffer? Scary!

Short answer is no. I just tried it. I performed a busy/release on the primary CDR link and the buffer is still at 40{0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0}. Unfortunately, the link is still down so I’ll have to check with the syslog team. But I just wanted to let all of you know that it’s safe to restart the link.

status cdr-link
                                CDR LINK STATUS
                   Primary                      Secondary

       Link State: down                         up
Number of Retries: 18
      Date & Time: 2015/09/23 06:35:59          2015/09/12 07:46:56
  Forward Seq. No: 0                            0
 Backward Seq. No: 0                            0
CDR Buffer {0ed28e3470e974017c124b0897303dd14e34b5245564abb28916e7d48d9b07c0} Full:  41.21                         0.00
      Reason Code: CDR connection is closed     OK


I ended up disabling and then enabling the TCP listener in Kiwi and the buffer immediately cleared. Is anyone interested in a post on how to capture CDR to a SYSLOG server?

Flowchart of Avaya Communication Manager Routing

Click Here for the flowchart PDFs

One might argue that telephone systems do only one thing. They route calls. In the process they turn lamps on and off, and they make telephones buzz and beep. But the only point is to route voices from point A to point B.

If you’ve been working with Avaya Communication Manager for a while, you have been exposed to AAR and ARS routing. And you hopefully have a fair grasp of how it all fits together. But there are a whole lot of decisions that are made when routing outbound calls. And if you’re new to Avaya, it might seem overwhelming to troubleshoot routing issues. So here are four pages of flowcharts that, based on my experience, are the most common configuration issues you’ll see when managing your telephone system. Click Here for the flowchart PDFs

Page 1 is a flowchart of the digit collection. This is what the CM does to collect the dialed digits, translate them as necessary, and select a network (AAR vs. ARS). Digits can be translated many times and re-pointed to different networks. Once this process is complete, the logic continues to page 2.

avaya_call_routing_flowchart-page_1_of_4

Page 2 is the process of using the final dialed digits (and station’s location) to select a route and a trunk group. There are many location and facilities restriction level checks here. And there is even more digit translation that can take place during this process. Once a trunk is selected and the call delivered, this may not be the end of it. Look Ahead Routing may pull the call back and try again.

avaya_call_routing_flowchart-page_2_of_4

Those two pages compose the primary routing logic that you will use in your typical troubleshooting. However, many decisions are based upon the calling party’s location. It may be obvious to you how the CM know’s the station’s location. But if things get strange, you can see how the CM associates a station to a location on page 3.

avaya_call_routing_flowchart-page_3_of_4

And lastly, there is one major headache associated with the “Prefix Mark” in a route pattern. Prior to 1995, there were many, many dialing rules associated with seven digit dialing, one plus seven digits, ten digits, etc. I remember living in a small town in the California Bay Area, and to dial Concord (over the hill), I had to dial 1+seven digits. Then when the area code 415 was split in to 415/510, we could dial 10 digits between them. Unfortunately, if you dialed 1+10 digits, it would be charged as a long distance call. All this went away in 1995 when the North America Numbering Plan was implemented. It is much easier to route calls now. But there is still the dreaded “Prefix Mark” that may still be in your routes. This causes havoc with the dialed-digits that are sent to your carrier. Page 4 is a summary of how this prefix mark works.

avaya_call_routing_flowchart-page_4_of_4

Please note there are two extremely important troubleshooting tools when dealing with Avaya routing. One, is the ‘list ars route-chosen’ and ‘list aar route chosen’ commands, and the other is the ‘list trace station’ and ‘list trace tac’ commands. If you use Avaya Site Administrator, you will not be able to use the ‘list trace’ command. I encourage you to use telnet or ssh in your day-to-day management of your PBX so you can use these commands when necessary. It may not sound like much, but being able to list trace at a moment’s notice is very helpful.

I will save the “list aar/ars route-chosen” and “list trace station/tac” for another posting. There’s a lot of information available in these commands.

Please let me know if you find these flowcharts helpful. Also, if you find any typos please let me know. Please keep in mind there are many more routing decisions the CM makes that are not in these flowcharts. If your CM uses other routing (for example tenants or time-of-day) and you think I should incorporate them into these flowcharts, please let me know! Happy routing everyone. This is really the core of any PBX, so I personally really enjoy working with routing.

Roger

Click Here for the flowchart PDFs

How to reboot the Avaya S8300D from the G-Series gateway

I recently had a problem logging into System Platform. I could not connect to the CDOM via the web, nor SSH into DOM0. Fortunately, there is a way to reboot the S8300D from the gateway itself. Log into the gateway as root, then type “configure” to get to the config menu. Then simply type “reset mm v1”, which will reboot the media module in slot V1. Confirm, and the card will reboot. From my understanding, it will cleanly shut down each VM and reboot.

As always, it will take just long enough for you to panic, plus 45 seconds or so.