Time-Zone Processing with Asterisk, Part II

 in
Part II of our series on time-zone processing with Asterisk.
Registration Event Handling

The register state handler is called whenever a SIP registration event is received from a new extension. Its main purpose is to get the data required for setting up the configuration telephone call when a new extension pops up. Whether a call is made depends on the state of the extension as far as time-zone processing, so this routine requests information to determine whether the extension is registered, its IP address and other components. To get the extension, we have to take the channel name, which is prefaced with the technology and a slash (for example SIP/) and strip the leading part away.

One wrinkle of the event handler is that POE handlers run to completion. There is no way to interrupt a handler when it is running. The sub-procedure getTZChannelVars will request information on the time-zone offset and IP address, but that information will not become available until the registration handler completes and the responses return via the manager. At the end of the procedure, the registration handler uses the delay_set POE method to queue up the call state for a delay in the future so that the requests will have returned their information by that point. The delay is set by a global variable in the program. I have found that one second is more than adequate for a single-user PBX with only one outstanding extension requiring setup, but the delay is set to three seconds for safety.

Communication between state handlers is a bit different from that in a procedure-driven program. POE state handlers pass references to the POE kernel, which is used in scheduling, as well as the POE heap, which is needed to issue commands to the Asterisk Manager. POE defines constants so the heap and kernel are easily accessible to event handlers as $_[HEAP] and $_[KERNEL]. Any other information available is located at $_[ARG0], which is a constant defined in such a way that it is the first argument.

Any lines in the event that defines the state will be passed as the hash $_[ARG0] and are accessible by asking for the hash key that appears on the left-hand side of the desired line. In the registration response, it is possible to get at the peer extension by referring to $_[ARG0]->{Peer}, which returns SIP/300:

Event: PeerStatus
PeerStatus: Registered
Peer: SIP/300

On SIP registration, the program needs to identify the extension, request information about it and then set up further processing of the extension data after a delay. When an event is called through the delay_set method, it is possible to pass an extension to the state handler, such as the extension number used here:

sub register_state_handler {
  my $kernel = $_[KERNEL];
  # Split peer extension off from technology
  my $peer = $_[ARG0]->{Peer};
  debug_print("\tExtension is $peer; ");
  my @exten_parts = split('/',$peer);
  my $ext = @exten_parts[1];
  debug_print("extension number is $ext\n");

  getTZChannelVars($_[HEAP], $ext);

  debug_print("Queuing call event for ");
  debug_print("$REG_CALL_DELAY seconds\n");
  $kernel->delay_set("call", $REG_CALL_DELAY, $ext);

} # register_state_handler

As part of the extension registration process, we collect variables about the state of the channel in the getTZChannelVars procedure. The POE heap, which is passed as the first argument, can be used to issue commands to the manager. For example, the put argument to the server can be used to issue commands. To get the SIP peer data, which includes the current IP address of the peer, the command looks like this:

$heap->{server}->put({'Action' => 'SIPShowPeer',
                      'Peer' => $ext });

To get a database variable, the action in the put command is a DBGet. The time-zone data is stored as keys in the tz family, so it is necessary to specify both the family and assemble the correct key name, which is of the form 300-TIMESKEW or similar:

$heap->{server}->put({'Action' => 'DBGet',
                      'Family' => 'tz',
                      'Key' => $ext . '-TIMESKEW'});

Four database requests and the SIP peer data are requested by getTZChannelVars. Because this function is called by an event handler, it also is not interruptible. Therefore, it sends four database query events to the manager, but it does not process responses directly. (Complete code for the five requests within the full procedure is available on the Linux Journal FTP site.)

Command and Database Responses

In the gap between issuing requests and the time the call state is scheduled, responses flow in from the SIP data request and the database queries. From the SIP data request, we need to pick out the peer IP address, which appears on a line in the manager response reading Address-IP: 192.168.1.5. Conveniently, the POE module parses out the lines in the response, so all we need to do is look for the Address-IP line by getting the value of the Address-IP hash element in one of the arguments passed to the handler. The POE heap is accessible across events, so adding the value of the SIP peer IP address to the heap makes it accessible to other event handlers:

sub response_state_handler {
  my $peer_ip = $_[ARG0]->{'Address-IP'};
  if (defined($peer_ip)) {
    debug_print("SIP context found; Peer IP address"
    debug_print("is $peer_ip\n");
    $_[HEAP]->{'SIP-Peer-IP'}=$peer_ip;
  }
} # response_state_handler

After the SIP data response comes back, the four database queries should return responses. Responses to the queries look like this:

DBGetResponse: Success
Family: tz
Key: 300-TIMESKEW
Val: -8

The callback handler is triggered whenever there is a DBGetResponse: Success event from the manager, with an argument of a hash that has each of the lines in the packet. Our interest is in the key and value lines, which can be retrieved from the arguments passed to the state handler. As with the previous handler, responses are stored in the POE task heap to make it available to other handlers:

sub db_response_state_handler {
  my $family = $_[ARG0]->{'Family'};
  my $key = $_[ARG0]->{'Key'};
  my $value = $_[ARG0]->{'Val'};

  if (defined($family)) {
     debug_print("Key $key in DB family $family");
     debug_print("has value = $value\n");
     # Store in heap
     $_[HEAP]->{$key} = $value;
  }
} # db_response_state_handler
______________________

White Paper
Linux Management with Red Hat Satellite: Measuring Business Impact and ROI

Linux has become a key foundation for supporting today's rapidly growing IT environments. Linux is being used to deploy business applications and databases, trading on its reputation as a low-cost operating environment. For many IT organizations, Linux is a mainstay for deploying Web servers and has evolved from handling basic file, print, and utility workloads to running mission-critical applications and databases, physically, virtually, and in the cloud. As Linux grows in importance in terms of value to the business, managing Linux environments to high standards of service quality — availability, security, and performance — becomes an essential requirement for business success.

Learn More

Sponsored by Red Hat

White Paper
Private PaaS for the Agile Enterprise

If you already use virtualized infrastructure, you are well on your way to leveraging the power of the cloud. Virtualization offers the promise of limitless resources, but how do you manage that scalability when your DevOps team doesn’t scale? In today’s hypercompetitive markets, fast results can make a difference between leading the pack vs. obsolescence. Organizations need more benefits from cloud computing than just raw resources. They need agility, flexibility, convenience, ROI, and control.

Stackato private Platform-as-a-Service technology from ActiveState extends your private cloud infrastructure by creating a private PaaS to provide on-demand availability, flexibility, control, and ultimately, faster time-to-market for your enterprise.

Learn More

Sponsored by ActiveState