Goal

This tutorial is intended for application developers who need more fine-grained control over the connections made between onePK applications and network elements.

In onePK, a session represents an authenticated channel of communication between an application and a network element. This tutorial demonstrates how to configure a session, get the properties and statistics of a session.

Tutorial Code

The code used in this tutorial is located under <SDK Location>/python/tutorials/session/.

Requirements/Prerequisites

This tutorial assumes the application can connect properly to a network element. Please see the Connecting to a Network Element tutorial for information on how to make the initial connection.

NOTE: You are recommended to use TLS (Transport Layer Security) to enable secure communications between your applications and connected devices. For information about configuring TLS on your connected network element and enabling onePK TLS communication, see onePK Security in the Getting Started with onePK guide.

Steps In Detail

Connecting to the Network Element

When connecting to a network element, the caller may optionally provide a SessionConfig that contains the required configuration for the resulting session. When creating the SessionConfig, the only required attribute is the transport mode.

  • TLS is the transport mode used for the end node hosting model. TLS (Transport Layer Security) provides encrypted communication with certificate authentication services for onePK applications. You are highly recommended to adopt the use of TLS for your application communications to connected devices. By default, the tutorial code runs with the TLS option set, with uni-directional authentication on the connected devices only. To ensure that your tutorial code can run using TLS, you need to:
    • Configure TLS on your router
    • Enable onePK TLS communication
    • Set the TLS communication option in your onePK application

For detailed information about setting up TLS communication, see onePK Security in the Getting Started with onePK Guide.

  • TIPC (sometimes referred to as LOCAL) may be used in process and blade hosting models. TIPC (Transparent Inter-Process Communication) is an open source protocol that enables applications in a clustered NOS environment to communicate with other applications in the cluster. TIPC provides location transparency of services in a network and reliable transport taking advantage of standard socket interface support. It also provides connectionless, connected-oriented, and multicast messaging. For more information about TIPC, see TIPC User Documentation at SourceForge.net.

In case of TLS verification failure, pinning can be used to proceed with connection. Pinning is the process by which certain hosts can be whitelisted for TLS verification. If the presented certificate matches the known certificate for that host in the pinning file/DB, that connection proceeds even if the certificate fails the customary TLS verification. Using a handler for unverified TLS hosts, an application may present a fingerprint of the unknown host to a user for manual verification. User can setTLSPinning on the config to make use of pinning feature.

All other attributes are optional, and will take on their default values if not explicitly set. To demonstrate reconnecting to the session, the reconnect timer will be set to one minute.

#  Construct a SessionConfig instance with the given transport mode. 
config = SessionConfig(mode)
#  Set the reconnect timer to one minute. 
config.reconnectTimer = 60
#  The session attributes below this point are set to their default
#  values.
#          
#  Set the port to connect to on the network element.
#  TLS      15002
#  TIPC     N/A
#          
if mode.lower() == "tls":
    config.port = config.DEFAULT_PORT
    config.transportMode = SessionConfig.SessionTransportMode.TLS
    config.ca_certs = tutorial.root_cert_path
    config.keyfile = tutorial.client_key_path
    config.certfile = tutorial.client_cert_path
else:
    #  Not required for TIPC.
    pass
#  Set the event queue size of the session. 
config.eventQueueSize = config.DEFAULT_EVENT_QUEUE_SIZE
#  Set the event thread pool size of the session. 
config.eventThreadPool = config.DEFAULT_THREADPOOL_SIZE
#  Set the event drop mode of the session. 
config.eventDropMode = config.DEFAULT_EVENT_DROP_MODE
#  Set the keepalive attributes of the session. 
#  Idle time in seconds 
config.keepAliveIdleTime = config.DEFAULT_KEEPALIVE_IDLE_TIME
#  Interval between keepalives in seconds 
config.keepAliveInterval = config.DEFAULT_KEEPALIVE_INTERVAL
#  Number of keepalives 
config.keepAliveRetryCount = config.DEFAULT_KEEPALIVE_RETRY_COUNT

Connect to the element using the given session configuration. If no configuration is specified by the caller (i.e. null is used), the session will take on the default values.

handle = networkElement.connect(tutorial.get_username(), tutorial.get_password(), config)

Upon receipt of a certificate which could not be verified, this handler asks the application whether to accept the connection and/or whether to add the host to the pinning database. By default, the connection will be terminated and the pinning db will remain unchanged. See a sample handler in below code snippet.

if  changed:
    msg = "\n@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"
    msg +="WARNING: THE CERTIFICATE PRESENTED BY REMOTE HOST '%s'\n IS DIFFERENT FROM THE ONE PREVIOUSLY ACCEPTED" %(host)
    msg +="\n@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
else:
    msg = "WARNING: Certificate presented by remote host '%s' is not verified."%(host)
msg += "\n\nThe %s fingerprint sent by the remote host(%s) is:\n%s" %(hashtype, host, finger_print)
msg += "\n\nYou MUST verify the certificate on remote host before proceeding! \n"
msg += "\nChoose from following options:"

if self.pinning_file:
    prompt = "\nAccept and Pin (p), Accept Once (o), Reject (r) (default) :"
else:
    prompt = "\nAccept Once (o), Reject (r) (default) :"

sys.stdout.write(msg)
self.decision = raw_input(prompt)

while True:
    if not self.decision or self.decision.lower() == 'r':
        return tlspinning.DecisionType.REJECT
    elif self.decision.lower() == 'p' and self.pinning_file:
        return tlspinning.DecisionType.ACCEPT_AND_PIN
    elif self.decision.lower() == 'o':
        return tlspinning.DecisionType.ACCEPT_ONCE
    else:
        self.decision = raw_input(prompt)

Upon a successful connection, a session is established and a handle is returned in the form of a SessionHandle. When a session is in the connected state, its configuration cannot be modified. The session handle may be used to query information about the session. Here, we use it to get the session's ID, which will be needed when we want to reconnect to the session.

    log = logging.getLogger('onep:SessionTutorial')
    log.setLevel(logging.INFO)
    retry = 3
    app_terminate = False

    def handle_event(self, event, data):

        self.log.info("\n********* CONNECT LISTENER *******")
        self.log.info('Received connection event %s',
                      event.elem.OnepSessionState.enumval(event.state))

        if self.app_terminate:
            self.log.info("\n********* TUTORIAL TERMINATED *******")
            return

        if event.state == event.elem.OnepSessionState.ONEP_STATE_DISCONNECTED:
            if not self.retry:
                self.log.info("\n********* RECONNECT RETRY MAX FOR %d *******" % data['id'])
                event.elem.set_connection_listener(None, None)
                return
            try:
                self.log.info("\n********* RECONNECT SESSION %d *******" % data['id'])
                event.elem.reconnect(data['user'], data['pwd'],
                                     data['id'], data['sess'])
            except Exception as e:
                self.retry -= 1
                self.log.info("\n********* RECONNECT FAILED SESSION %d*******" %data['id'])
                self.log.info("\n********* %s *******" % str(e))

Build a listener class to react to application connection events. The connection listener will be registered directly to the instantiated NetworkElement class.

In this example we have setup a log to send messages to the application logger. We have also added a maximum reconnect retry count and a flag to tell the listener when the application wants to exit without a reconnect attempt.

sessionID = originalSessionHandle._id

The session handle can also be used to get properties of the session and statistics about the session. Here, we just print them out.

 #  Get the property instance for this session using the
 #  session handle.
 # 
 property = handle.sessionProp

 #  Get the port number the session is connected on. 
 logger.info("Port: " + str(property.port))
 #  Get the event queue size of the session. 
 logger.info("EventQueueSize: " + str(property.eventQueueSize))
 #  Get the event thread pool size of the session. 
 logger.info("EventThreadPool: " + str(property.eventThreadPool))
 #  Get the event drop mode of the session. 
 logger.info("EventDropMode: " + str(property.eventDropMode))
 #  Get the reconnect timer of the session in seconds. 
 logger.info("ReconnectTimer: " + str(property.reconnectTimer))
 #  Get the transport mode of the session. 
 logger.info("TransportMode: " + str(property.transportMode))
#  Get the statistics instance for this session using the
#  session handle.
#          
statistics = handle.sessionStat
#  Get the count of events received and dropped. 
logger.info("Events Total: %s", statistics.eventTotalCount)
logger.info("\nEvents Dropped: %s", statistics.eventDropCount)
con_listener = TutorialReconnectListener()
tutorial.get_network_element().set_connection_listener(con_listener,
                                                       {'user': tutorial.get_username(),
                                                        'pwd': tutorial.get_password(),
                                                        'id': sessionID,
                                                        'sess' : config})

Register your connection listener to the network element.

Result

Congratulations! You have configured a session, read the properties and statistics of a session, and reconnected to a session.

To try out this tutorial code by compiling and running it, you can find the code located at: <SDK Location>/python/tutorials/session/.