Subscribing to Device Change Notifications

The Junos Space platform monitors its network devices and updates a common data model whenever there is a change in state of a device, such as with the device's configuration or inventory. Junos Space applications can subscribe to device change notifications so that whenever a device state changes, the subscribed application receives a notification. The application can use this information about the change to update their own data models.

The state of a device includes its:

Two mechanisms are available for subscribing to device change notifications:

REST API Subscriptions

Subscribing to device changes

Subscribe to device change notifications using REST APIs. Once the subscription is registered, notifications are generated for the subscribed XPaths whenever the device changes. This XPath refers to node changes in the states for any of the managed devices. The xpath-filter element takes XML as a string specifying the XPath filters. This input XML should comply with the schema. See the following example:

HTTP Request

HTTP POST /api/space/device-management/subscribe-change-notifications 
Content-Type: application/vnd.net.juniper.space.device-management.
              subscribe-change-notifications+xml;version=1;charset=UTF-8


<subscribe-change-notifications>
  <appName>REST</appName>
  <xpath-filter><![CDATA[
<app-interest-spec xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:noNamespaceSchemaLocation="xml-app-interest-spec.xsd">
  <app-name>REST</app-name>
  <schemas>
    <schema type="system-information">
      <dmi-node xpath="/system-information" type="STRING" />
      <dmi-node xpath="/system-information/host-name" type="STRING" />
    </schema>
  </schemas>
</app-interest-spec>
]]>
  </xpath-filter>
</subscribe-change-notifications>

HTTP Response

200 OK

Description of app-interest XSD

The following table describes the appropriate nodes and attributes for app-interest XSD. (Note: Nodes are shown in bold text, and required attributes are listed under related nodes.) For more information, see XSD schema.

NODES DESCRIPTION
app-interest-spec This node is the root element of the registration file.
app-name This attribute specifies the name of the application registering the xpaths. For registration using REST, it is just a string to identify your registration.
schemas This node is the container for the schema nodes described below.
schema This represents the schema node.
device-family This attribute represents the device's family.
name This attribute is a simple string representing the name for the collection of xpaths.
override This attribute overrides the name common to more than one device family to list it separately.
type This attribute represents the value of the configuration. It can have any of these values: configuration, interface-inventory, logical-interface-inventory, hardware-inventory, software-inventory, license-inventory, system-information.
dmi-node This node registers the xpath of elements.
blob This attribute registers the child nodes of a particular node to receive the change notification.
exclude This attribute excludes the xpath from the blob node.
key This attribute represents the key node registered for the collection type.
type This attribute represents the type of the node for a configuration schema xpath. It can be either string or collection.
override This attribute overrides the name common to more than one in the registered xpath schema.
preserve This attribute preserves a single or all child nodes in different results even if they have changed or not. For a single child node, you provide the value as "SELF", and for all the child nodes, you can provide RECURSIVE.
xpath This attribute registers the device knobs for change notifications.

You can a create a HornetQ queue over REST using the /api/hornet-q service. For more information about creating a queue, see "Enabling Notifications" in the Junos Space SDK Application Developer Guide.

HTTP Request

HTTP POST /api/hornet-q/queues
Content-Type: application/hornetq.jms.queue+xml

<queue name="testq">
	<durable>false</durable>
</queue>

HTTP Response

201 Created

Subscribing to a "device-change" topic

After creating a HornetQ queue, the REST client needs to subscribe to a device-change topic and use a selector for the JMS Property interested_app_names. The selector should be the same as the app-name provided when registering for the device change notification. See the following example:

HTTP Request

HTTP POST /api/hornet-q/topics/jms.topic.device-change/push-subscriptions	
Content-Type : application/xml	
	

<push-topic-registration>
  <durable>false</durable>
  <selector><![CDATA[
  interested_app_names like '%REST%'
    ]]>
  </selector>
  <link type="application/xml" rel="destination" href="http://127.0.0.1:8080/api/hornet-q/queues/jms.queue.testq" />
</push-topic-registration>

HTTP Response

201 Created

Receiving notification on a "device-change" topic

After subscribing to a device-change topic, REST clients should poll the HornetQ queue. They will receive DeviceChangeDiffResult as XML or JSON on the queue when any change occurs on the registered XPath nodes. To learn about polling a HornetQ queue, see "Enabling Notifications" in the Junos Space SDK Application Developers Guide.

Message-Driven Bean (MDB) Subscriptions

Registering for device change notifications using XML

Alternatively you can register for device change notifications using a static XML file.

  1. Create an XML file with the name ${your-app-name}-interest-spec.xml. This XML should comply with the schema. For example, SampleApp-interest-spec.xml.
  2. <app-interest-spec xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    	xsi:noNamespaceSchemaLocation="xml-app-interest-spec.xsd">
    <app-name>Sample_App</app-name>
    <schemas>
    
      <!-- Your xpath node filter should go here for example
      <schema type="configuration" device-family="junos">
    	<dmi-node xpath="/configuration/groups/name"/>
    	<dmi-node xpath="/configuration/groups/rcsid"/>
    	<dmi-node xpath="/configuration/groups/system"/>
      </schema>
      -->
    
     </schemas>
    </app-interest-spec>
    
    
  3. Place this file in the META-INF/device-change-notification folder within your EAR file.

Receiving notifications on MDB

The Junos Space platform sends device change notifications to Junos Space applications through the JMS. The platform uses a single topic-named device change to broadcast device changes to all subscribed applications. To receive device change notifications for your application, an MDB is created to listen to the device-change topic.

The platform provides a base MDB to listen to the device-changeBaseMDB topic. Applications interested in receiving the device change notification need to create their own MDB to listen to this device-change topic. This MDB should extend from the base MDB (device-changeBaseMDB) and implement the following abstract methods:

METHOD DESCRIPTION
boolean handleDeviceChange(DeviceChangeDiffResult diffResult) Allows applications to process device change results. It would have a filtered diff based on the XPath provided during registration.
void setDeviceStatus(Integer deviceId, ApplicationDeviceStatus status) Allows Junos Space applications to set the status of their (application specific) device as 'out of sync' or 'in sync'. On receiving the message, the base MDB first calls the setDeviceStatus method to mark the device as ‘out-of-sync’ by setting the status parameter to ApplicationDeviceStatus.DEVICE_STATE_OUT_OF_SYNC.
String getApplicationName() Allows you to request the application name. Ensure that it exactly matches the one specified in the app-interest registration file under the <app-name> node.

When a device change message is received, the MBD calls the setDeviceStatus method and sets the status parameter to ApplicationDeviceStatus.DEVICE_STATE_OUT_OF_SYNC. The setDeviceStatus method allows the application to set its own device status in the database as either "out of sync" or "in sync". The application should have its own device entity.

Next handleNotification() is called, passing the DeviceChangeDiffResult result. The DeviceChangeDiffResult method retrieves the different XML strings and XML differences. The applications can process the device change diff result inside the handleDeviceChange method.

Finally, on a successful response, the setDeviceStatus method is called again to mark the state of the device as "in-sync". Therefore, the status parameter is returned as ApplicationDeviceStatus.DEVICE_STATE_IN_SYNC.

@MessageDriven(name = "SampleAppMDB",
   activationConfig = {
     @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Topic"),
     @ActivationConfigProperty(propertyName = "destination", propertyValue = "topic/device-change"),
     @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"),
     @ActivationConfigProperty(propertyName = "subscriptionDurability", propertyValue = "NonDurable"),
     @ActivationConfigProperty(propertyName = "messageSelector", propertyValue= 
                                              "interested_app_names LIKE '%Sample_App%'")
   })

public class SampleAppMDB extends net.juniper.jmp.cmp.deviceChange.DeviceChangeBaseMDB
{
 public boolean handleDeviceChange( DeviceChangeDiffResult result )
  {
  /* result parameter will have filtered difference based on xpath 
     provided during device-change registration, 
  */ 
  return true;
  }
  public void setDeviceStatus(Integer deviceId, ApplicationDeviceStatus status)
  {
   /* set the status of (application specific) device as 'out of sync' or 'in sync'*/
  }
  public String getApplicationName()
  {
   /* Application name should match the name specified in 
      app interest registration file under <app-name> node.
   */
   return "Sample_App";
 }	
}

Here is the outline of the DeviceChangeDiffResult class:


DeviceChangeDiffResult{
	private Integer deviceId;
	private String diffResults;
	public Integer getDeviceId() {
        return deviceId;
   	}	
	/* first element in map represents the schemaName and the second xml diff*/
	public Map<String, String> getDiffResults() {
        return diffResults;
    }
    /* Returns list of xpaths that has changed in this diff result */
    public Set<string> getChangedXPaths(){
		//implementation
 } 
}

MBD override methods list nodes that are common to more than one device family. For example, if an application wants to register the following xpaths for both the junos and ext-junos device families.

You can list these xpaths under a common schema node and then include this common node for both device families with the override attribute as shown in the following example.


<app-interest-spec xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="xml-app-interest-spec.xsd">
<app-name>CEMS</app-name>
    <schema type="configuration" name="common-nodes" >
      <dmi-node xpath="/configuration/routing-instances/instance/name"/>
      <dmi-node xpath="/configuration/routing-instances/instance/instance-type"/>
    </schema>

<schema type="configuration" device-family="junos" override="common-nodes">
         /*other dmi xpaths for  junos */
            ...
            ...
    </schema>

    <schema type="configuration" device-family="ext-junos" override="common-nodes">
      <dmi-node xpath="/configuration/vlans/vlan/name"/>
         /*other dmi xpaths for  ext-junos*/
            ...
            ...
   </schema>
</app-interest-spec>

Note: The common schema node must have a name. The same name should be specified as a value of the override attribute.

app-interest XSD

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <xs:element name="app-interest-spec" type="appInterestRegistration"/>

  <xs:complexType name="appInterestRegistration">
    <xs:sequence>
      <xs:element name="app-name" type="xs:string" minOccurs="0"/>
      <xs:element name="schemas" minOccurs="0">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="schema" type="schemaNode" minOccurs="0" maxOccurs="unbounded"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="schemaNode">
    <xs:sequence>
      <xs:element name="dmi-node" type="dmiNode" minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
    <xs:attribute name="device-family" type="xs:string"/>
    <xs:attribute name="name" type="xs:string"/>
    <xs:attribute name="override" type="xs:string"/>
    <xs:attribute name="type" type="xs:string" use="required"/>
  </xs:complexType>

  <xs:complexType name="dmiNode">
    <xs:sequence>
      <xs:element name="exclude" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
    <xs:attribute name="blob" type="xs:boolean"/>
    <xs:attribute name="exclude">
      <xs:simpleType>
        <xs:list itemType="xs:string"/>
      </xs:simpleType>
    </xs:attribute>
    <xs:attribute name="key">
      <xs:simpleType>
        <xs:list itemType="xs:string"/>
      </xs:simpleType>
    </xs:attribute>
    <xs:attribute name="type" type="xs:string"/>
    <xs:attribute name="override" type="overrideDmiNodeEnum"/>
    <xs:attribute name="preserve" type="preserveDmiNodeEnum"/>
    <xs:attribute name="xpath" type="xs:string" use="required"/>
  </xs:complexType>

  <xs:simpleType name="overrideDmiNodeEnum">
    <xs:restriction base="xs:string">
      <xs:enumeration value="REMOVE"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="preserveDmiNodeEnum">
    <xs:restriction base="xs:string">
      <xs:enumeration value="SELF"/>
      <xs:enumeration value="RECURSIVE"/>
    </xs:restriction>
  </xs:simpleType>
</xs:schema>