Changing the CDRs generated by Sentinel

Sentinel defines the CDR format, and a number of mappers for building CDRs using the code generated by the Google protobuf compiler.

You have two options for generating your own CDRs:

Option number one is the preferred and recommended approach.

Tip For more details on mappers, see the Mappers overview.
Note The examples given assume the SDK was installed for (vendor=Acme Inc, vendorKey=acme, version=1.5)

CDR Mapper Module Packs

Sentinel Express includes three module packs that can be used to extend Sentinel CDRs:

  • sentinel-diameter-cdr-mappers for Diameter.

  • sentinel-ss7-cdr-mappers for SS7.

Creating a Module Pack

To view the available module-packs, type the following command inside the sdkadm tool:

list-modules +module-pack
Note Where you see a version number of 4.1.0 use the version number of the product you have downloaded.

Now create a new module from the module pack published in opencloud#sentinel-diameter-cdr-mappers#sentinel-diameter/4.1;4.1.0. This requires two steps:

1

Run the command:

create-module acme-diameter-cdr-mappers opencloud#sentinel-diameter-cdr-mappers#sentinel-diameter/4.1;4.1.0

This command:

  • downloads the module pack from the repository

  • creates a new directory called acme-diameter-cdr-mappers inside your Sentinel Express SDK, which contains all the newly created modules

  • scans the content of the module pack and prompts you to enter new values where necessary

  • re-writes the new modules according to your answers.

When prompted, answer as shown in the example output below. Numbered annotations mark the prompts, and their answers are listed by number immediately after the example output.

> create-module acme-diameter-cdr-mappers opencloud#sentinel-diameter-cdr-mappers#sentinel-diameter/4.1;4.1.0
downloading https://repo.rhino.metaswitch.com/artifactory/opencloud-internal-snapshots/opencloud/sentinel-diameter/4.1/sentinel-diameter-cdr-mappers/4.1.0/sentinel-diameter-cdr-mappers-module-pack-4.1.0.zip ...
... (18kB)
.. (0kB)
        [SUCCESSFUL ] opencloud#sentinel-diameter-cdr-mappers#sentinel-diameter/4.1;4.1.0!sentinel-diameter-cdr-mappers-module-pack.zip(module-pack) (81ms)
Extracting '/home/testuser/sentinel-express-sdk/build/target/ivy-caches/online-resolvers.cache/opencloud/sentinel-diameter-cdr-mappers/sentinel-diameter/4.1/module-packs/sentinel-diameter-cdr-mappers-module-pack-4.1.0.zip' to '/home/testuser/sentinel-express-sdk/acme-diameter-cdr-mappers'.


Command line invocation did not contain enough rename arguments to rename all modules.
To specify rename arguments on the command line, include <oldvalue>:<newvalue> pairs as additional arguments.
Missing values will now be prompted for interactively.

Please enter a name for the top level module, usually this will match the name of the directory for the new module
Rename top level module 'sentinel-diameter-cdr-mappers' to [acme-diameter-cdr-mappers]: 1

Please enter names for the following sub-module(s) in the module-pack
Rename module 'sentinel-diameter-cdr-format' to [sentinel-diameter-cdr-format]: acme-diameter-cdr-format 2
The longest common package prefix is 'com.opencloud.sentinel'.
Rename package prefix 'com.opencloud.sentinel' to [com.opencloud.sentinel]: com.acme.sentinel 3

Command line invocation did not contain enough rename arguments to rename all mappers.
To specify rename arguments on the command line, include <oldvalue>:<newvalue> pairs as additional arguments.
Missing values will now be prompted for interactively.

Rename mapper 'CCRToCDR' to [CCRToCDR]: AcmeCCRToCDR 4

Re-writing source files with new package declarations.

Renaming ivy modules and updating dependencies.

Renaming symbolic property references in source files.
Checking "deps.properties" for missing values.

Done. New module(s) should now be available at: /home/testuser/sentinel-express-sdk/acme-diameter-cdr-mappers
1 Press Enter to accept the default
2 Type acme-diameter-cdr-format and press Enter
3 Type com.acme.sentinel and press Enter
4 Type AcmeCCRToCDR and press Enter
Note It is possible to use the command in a non-interactive mode by providing all substitution values.
Run help create-module for instructions.

Adding Fields to Sentinel CDRs

Extending Google protocol buffers

Google protocol buffers may be extended with additional fields, without prior knowledge, by using the protocol buffer extensions mechanism.

Extensions let you declare that a range of field numbers in a message are available for third-party extensions. Other people can then declare new fields for your message type with those numeric tags in their own .proto files without having to edit the original file.

The Sentinel protocol buffer CDR definitions define a range of field numbers for extensions.

  • The CDR .proto files for Diameter are artifacts of the sentinel-diameter-cdr-format module.

  • The CDR .proto files for SS7 are artifacts of the sentinel-ss7-cdr-format module.

Here is an excerpt from the Diameter sentinel-diameter-cdr-format.proto file:

package com.opencloud.sentinel.cdr;

message DiameterChargingCdr {

// ...

extensions 50 to max;

So field numbers 50 (and above) may be used for your extensions.

Defining your own fields

After creating a module from sentinel-diameter-cdr-mappers or sentinel-ss7-cdr-mappers you can add any CDR fields in the acme-diameter-cdr-format and acme-ss7-cdr-format *.proto files.

package com.acme.sentinel.cdr;

import "com/opencloud/sentinel/cdr/base-cdr-types.proto";

// Diameter Service CDR

message DiameterChargingCdr {

    optional string subscriber = 1;
    optional int64 initiatingRequestId = 2 [default = -1];
    optional com.opencloud.sentinel.cdr.Time eventInitiated = 3;
    optional com.opencloud.sentinel.cdr.Time sessionInitiated = 4;
    optional com.opencloud.sentinel.cdr.Time sessionEnded = 5;
    optional com.opencloud.sentinel.cdr.CdrSessionCounters sessionCounters = 6;

    repeated int32 ocsLatencySamples = 19 [packed=true];
    optional string clientSessionId = 20;
    repeated string ocsSessionIDs = 21;

    optional string sentinelSelectionKey = 22;

    optional ReleaseReason releaseReason = 23;

    extensions 50 to max;

    message ReleaseReason {

        optional string cause = 1;
        optional string description = 2;
    }

}

Here we define a structure for the ReleaseReason that has two fields, a cause and a description. One of the artifacts generated by the acme-diameter-cdr-format module is acme-diameter-cdr-format.jar, which contains the compiled code generated by the Google protobuf compiler for your fields.

Using your fields in CDR mappers

Sentinel provides a number of Mappers for generating CDRs. These mappers can be extended by the SDK to include any additional fields you have defined. The Sentinel SDK includes mapper modules for Diameter and SS7, which lets you extend existing mappers. These modules include custom mapper implementations for adding support for your own fields.

In the acme-diameter-cdr-mappers module see:

  • com.acme.sentinel.cdr.mapper.CCRtoCDR

In the acme-ss7-cdr-mappers module see:

  • com.acme.sentinel.cdr.mapper.CAP1InitialDPArgToCdr

  • com.acme.sentinel.cdr.mapper.CAP3InitialDPSMSArgToCdr

  • com.acme.sentinel.cdr.mapper.CS1InitialDPArgToCdr

  • com.acme.sentinel.cdr.mapper.DialogOpenRequestEventToCdr

  • com.acme.sentinel.cdr.mapper.EventTypeBCSMToCdrConnectCause

  • com.acme.sentinel.cdr.mapper.HttpRequestToSS7CallCdr

Each mapper defines a protected method you override to add your own mapping behaviour. For example, to add the release cause and description to the CDR in the Diameter service update the AcmeCCRtoCDR mapper like this:

    /**
     * Add extensions to the generated CDR
     *
     * @param cdrBuilder the protobuf builder for the CDR
     * @param intiatingCCR the CCR that initiated the session
     * @param ss the sentinel session state
     * @param facilities facilities for tracing and so on
     */

    protected void addCdrExtensions(DiameterChargingCdr.Builder cdrBuilder, CreditControlRequest intiatingCCR, SentinelDiameterSessionState ss, MapperFacilities facilities) throws MapperException {

        // to be overridden by SDK defined mappers
        // get values to include from session state
        final String releaseCause = "extensionReleaseCause";
        final String releaseDescription = "extensionReleaseDescription";

        DiameterChargingCdr.ReleaseReason.Builder rrBuilder = DiameterChargingCdr.ReleaseReason.newBuilder();
        // construct ReleaseReason field for the CDR
        rrBuilder.setCause(releaseCause);
        rrBuilder.setDescription(releaseDescription);

        cdrBuilder.setReleaseReason(rrBuilder.build());

    }

Creating your own CDRs

To create your your own CDRs you:

Write your own CDR protobuf definition

You can also refer to the following Sentinel protobuf artifacts for inspiration:

  • In the acme-diameter-cdr-format module:

target/libs/self/sentinel-cdr-common-base-cdr-types-*.proto

src/com/acme/sentinel/cdr/sentinel-diameter-cdr-format.proto

  • In the acme-ss7-cdr-format module:

target/libs/self/sentinel-cdr-common-base-cdr-types-*.proto

src/com/acme/sentinel/cdr/sentinel-ss7-cdr-format-*.proto

src/com/acme/sentinel/cdr/sentinel-ss7-sms-cdr-format-*.proto

The simplest approach is to update the *.proto file in the com.acme.sentinel.cdr package. Remove the import statement (that corresponds to the Sentinel core CDR format) and create your own protobuf definition. If you adopt this approach then you do not need to change build.xml or change any dependencies.

Write your own CDR mappers

As mentioned in Using your fields in CDR mappers, each mappers module in the SDK includes custom CDR mapper implementations for adding support for your own fields. You can edit these mappers by:

  1. removing the extends clause

  2. implement the Mapper interface directly

For example, in the acme-ss7-mappers module:

/**
 * Copyright (c) 2014 Open Cloud Limited, a company incorporated in England and Wales (Registration Number 6000941) with its principal place of business at Edinburgh House, St John's Innovation Park, Cowley Road, Cambridge CB4 0DS.
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
 *
 * 1  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 *
 * 2  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
 *
 * 3  The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.
 *
 * 4  The source code may not be used to create, develop, use or distribute software for use on any platform other than the Open Cloud Rhino and Open Cloud Rhino Sentinel platforms or any successor products.
 *
 * 5  Full license terms may be found https://developer.opencloud.com/devportal/display/OCDEV/Feature+Source+License
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF SATISFACTORY QUALITY AND FITNESS FOR A PARTICULAR PURPOSE ARE EXCLUDED TO THE FULLEST EXTENT PERMITTED BY LAW.
 *
 * TO THE FULLEST EXTENT PERMISSIBLE BY LAW, THE AUTHOR SHALL NOT BE LIABLE FOR ANY LOSS OF REVENUE, LOSS OF PROFIT, LOSS OF FUTURE BUSINESS, LOSS OF DATA OR ANY INDIRECT, SPECIAL, CONSEQUENTIAL, PUNITIVE OR OTHER LOSS OR DAMAGES ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE, WHETHER ARISING IN CONTRACT, TORT (INCLUDING NEGLIGENCE) MISREPRESENTATION OR OTHERWISE AND REGARDLESS OF WHETHER OPEN CLOUD HAS BEEN ADVISED OF THE POSSIBILITY OF ANY SUCH LOSS OR DAMAGE. THE AUTHORS MAXIMUM AGGREGATE LIABILITY WHETHER IN CONTRACT, TORT (INCLUDING NEGLIGENCE) OR OTHERWISE, SHALL NOT EXCEED EUR100.
 *
 * NOTHING IN THIS LICENSE SHALL LIMIT THE LIABILITY OF THE AUTHOR FOR DEATH OR PERSONAL INJURY RESULTING FROM NEGLIGENCE, FRAUD OR FRAUDULENT MISREPRESENTATION.
 *
 * Visit Open Cloud Developer's Portal for how-to guides, examples, documentation, forums and more: http://developer.opencloud.com
 */
package com.opencloud.sentinel.cdr.mapper.impl;

import com.google.protobuf.Message;
import com.opencloud.sentinel.annotations.Mapping;
import com.opencloud.sentinel.annotations.SentinelMapper;
import com.opencloud.sentinel.cdr.SentinelSs7CdrFormat.Ss7CallCdr;
import com.opencloud.sentinel.common.SentinelSs7SessionState;
import com.opencloud.sentinel.mapper.MapperException;
import com.opencloud.sentinel.mapper.MapperFacilities;
import com.opencloud.sentinel.util.LongByteArray;
import com.opencloud.slee.resources.cgin.callcontrol.CCCallInformationReportArg;
import com.opencloud.slee.resources.cgin.cap_v1.CAP1InitialDPArg;
import com.opencloud.slee.resources.cgin.cap_v2.CAP2CallInformationReportArg;
import com.opencloud.slee.resources.cgin.cap_v2.CAP2InitialDPArg;
import com.opencloud.slee.resources.cgin.cap_v3.CAP3InitialDPArg;
import com.opencloud.slee.resources.cgin.cap_v4.CAP4InitialDPArg;
import com.opencloud.slee.resources.in.datatypes.cc.LegType.EncodedValue;

/**
 * Maps {@link com.opencloud.slee.resources.cgin.cap_v1.CAP1InitialDPArg}, {@link com.opencloud.slee.resources.cgin.cap_v2.CAP2InitialDPArg}, {@link com.opencloud.slee.resources.cgin.cap_v3.CAP3InitialDPArg} or {@link com.opencloud.slee.resources.cgin.cap_v4.CAP4InitialDPArg} to
 * a google protobuf {@link com.google.protobuf.Message}.
 */
@SentinelMapper(mappings = { @Mapping(name = "CAP1InitialDPArgToCdr", fromClass = CAP1InitialDPArg.class, toClass = Message.class),
        @Mapping(name = "CAP2InitialDPArgToCdr", fromClass = CAP2InitialDPArg.class, toClass = Message.class),
        @Mapping(name = "CAP3InitialDPArgToCdr", fromClass = CAP3InitialDPArg.class, toClass = Message.class),
        @Mapping(name = "CAP4InitialDPArgToCdr", fromClass = CAP4InitialDPArg.class, toClass = Message.class) })
public class CAP1InitialDPArgToCdr extends BaseCCInitialDPArgToCdr {

    @Override
    public Object map(Object arg, SentinelSs7SessionState sessionState, MapperFacilities facilities) throws MapperException {

        final Ss7CallCdr.Builder cdrBuilder = map(sessionState, facilities);

        final CAP1InitialDPArg idp = (CAP1InitialDPArg) arg;

        // This method on the super class fills in all builder fields which are common to both CS1 and CAP
        populateCommonCDRFields(idp, sessionState, facilities, cdrBuilder);

        //
        // CAP specific logic start here.
        //

        // Called Party Number
        switch (sessionState.getCallType()) {
        case MobileOriginating:
        case EmergencyCall:
        case NetworkInitiated:
            if (idp.hasCalledPartyBCDNumber())
                cdrBuilder.setCalledPartyNumber(idp.getCalledPartyBCDNumber().getAddress());
            break;
        default:
            if (idp.hasCalledPartyNumber())
                cdrBuilder.setCalledPartyNumber(idp.getCalledPartyNumber().getAddress());
        }

        // MSC address
        if (idp.hasMscAddress())
            cdrBuilder.setMscNumber(idp.getMscAddress().getAddress());

        // Tele or Bearer Service
        if (idp.hasExt_basicServiceCode() && idp.getExt_basicServiceCode().isExt_TeleserviceChosen())
            cdrBuilder.setTeleService(idp.getExt_basicServiceCode().getExt_Teleservice()[0]);

        if (idp.hasExt_basicServiceCode() && idp.getExt_basicServiceCode().isExt_BearerServiceChosen())
            cdrBuilder.setBearerService(idp.getExt_basicServiceCode().getExt_BearerService()[0]);

        // Event initiated time - time event emitted by client (in this case idp.TimeAndTimezone)
        if (idp instanceof CAP2InitialDPArg) {
            CAP2InitialDPArg cap2idp = (CAP2InitialDPArg) idp;
            if (cap2idp.hasTimeAndTimezone()) {
                cdrBuilder.setEventInitiated(getCdrTime(cap2idp.getTimeAndTimezone().toCalendar().getTimeInMillis(), cap2idp.getTimeAndTimezone().toCalendar().getTimeZone()));
            }
        }

        // Call reference number
        if (idp.hasCallReferenceNumber())
            cdrBuilder.setCallReferenceNumber(LongByteArray.getLongVal(idp.getCallReferenceNumber()));

        // add any extensions to the CDR
        addCdrExtensions(cdrBuilder, idp, sessionState, facilities);

        return cdrBuilder.build();
    }

    /**
     * Add extensions to the generated CDR
     *
     * @param cdrBuilder the protobuf builder for the CDR
     * @param idp the InitialDP that initiated the session
     * @param ss the sentinel session state
     * @param facilities facilities for tracing and so on
     */
    protected void addCdrExtensions(Ss7CallCdr.Builder cdrBuilder, CAP1InitialDPArg idp, SentinelSs7SessionState ss, MapperFacilities facilities) throws MapperException {
        // to be overridden by SDK defined mappers
    }

    @Override
    protected EncodedValue getLegType(CCCallInformationReportArg cirp) {
        if (cirp instanceof CAP2CallInformationReportArg)
            return ((CAP2CallInformationReportArg) cirp).getLegID().getReceivingSideID().getEncodedValue();
        // Assume called party leg for CS1 as the report is comparable to CAMEL's report for this leg
        return EncodedValue.CALLED_PARTY;
    }
}
Previous page Next page