NorthStar API Developer Guide

Introduction

NorthStar API Basics

Using the NorthStar API

Introduction

Welcome to Juniper NorthStar API.

Juniper Networks® NorthStar WAN SDN Controller is a flexible traffic engineering (TE) solution, which delivers visibility and control over IP/MPLS and optical layer flows for WAN analysis and optimization. NorthStar combines Juniper Networks Junos® operating system carrier-grade peering capabilities with WANDL IP/MPLSView multilayer optimization expertise in a unified, highly integrated controller package, which:

  • Streamlines capacity planning.

  • Enables proactive monitoring.

  • Permits service providers to dynamically route traffic and balance loads based on administratively defined policies.

Using the NorthStar API, network engineers and application programmers can develop custom network optimization applications that extend UI capabilities to accommodate unique business requirements and network configurations. The major benefits are:

  • Global optimization of Label Switched Path (LSP) placement. Because NorthStar is coupled to the live network, the placement decisions can be triggered by actual traffic levels.

  • Path Computation to meet specific service requirements.

  • Operation automation.

As examples, you might use the API to compute the data center backup LSP based on the lowest cost directly correlated with a more economical pricing model to the customer. Conversely, a premium-cost LSP set up during peak business hours might have its path computed based on the lowest delay, highest availability, and most diverse path.  The API might also be used to enable new processes and workflows, such as a customer self-service portal, where the end user logs in to request an LSP be set up on demand, or scheduled without operator intervention.

The NorthStar Controller Web User Interface provides a UI for visualizing current LSP paths and LSP past history, whereas the API provides programmatic access to those same entities as a foundation for building more, centralized, network infrastructure services.

NorthStar supports southbound and northbound interfaces, as shown:

The southbound interface connects to routers and switches using ISIS, OSPFv2, BGP-LS, and PCEP protocols. The northbound interface is an API for applications to retrieve, subscribe to, and modifying network configuration and management data.

The API is a RESTful implementation, which simplifies integration of command-line, browser, or popular programming language tools and applications with NorthStar Controller functionality, which includes:

  • Listing topology.

  • Getting complete node, link, and LSP information for the entire topology.

  • Getting complete information about a node, link, or LSP.

  • Submitting an LSP provisioning request.

  • Adding, deleting, or modifying a node, link, or LSP.

Additionally, the API provides a mechanism for asynchronous notification so you can be notified by the NorthStar Controller of the occurrence of specific network events.

The purpose of this document is to give you a basis for understanding and using the API to integrate with the NorthStar Controller.

Intended Audience

Experienced application programmers and system and network specialists working with the NorthStar Controller in an Internet access environment can find useful guidance in this document, for developing management and monitoring applications using the NorthStar API.

This document assumes familiarity with NorthStar Controller functionality and with networking concepts, specifically, IP/MPLS and optical layer traffic engineering in the context of a Path Computation Element (PCE) architecture.

As a prerequisite for writing applications using the API, you should also have a basic understanding of RESTful HTTP concepts, JSON data formats, and HTTP programming libraries for your programming language of choice.

How to Use This Guide

This guide provides instructional and contextual information needed to use the NorthStar API. For detailed, syntactic information about the interface, itself, and specific endpoints see the NorthStar API Reference.

The examples and tutorials in this document provide a general introduction to the API, which demonstrate common use cases that can serve as a basis for quickly developing custom applications. Simple API call examples use the cURL CLI because it is readily available and easy to use. More extensive code solutions are shown using the Python programming language. You can port these examples to a programming language that supports the HTTP interface.

Support

Visit the On-line Support (CSC) page for more support.

Quick Reference

Resource Groups

Resources are grouped by feature or network entity.

Resource Group Base URI Description
High availability /highAvailability Monitor and configure high availability cluster.
Device profiles /netconf/profiles Access device profile information.
Device collections ​/netconf/netconfCollection Access device collection information.
Topology /topology Access topology information.
Links ​/topology/​{topology_id}​/links Access link information.
Nodes /topology/​{topology_id}​/nodes Access nodes retrieved through BGP-LS and planned nodes.
Facilities /topology/​{topology_id}​/facilities Access facilities information.
P2MP groups ​/topology/​{topology_id}​/p2m Access P2MP group information.
Path computation ​/topology/​{topology_id}​/pathComputation Compute path on the TE topology.
TE-LSPs /topology/​{topology_id}​/te-lsps Access TE-LSP information.
Topology acquisition /transportControllers Transport controller connection configuration and topology retrieval.
Transport controller groups /transportControllerGroups Manage set of transport controller instances.

Resource Endpoints

URI Description POST PUT GET DELETE
High availability
​/highAvailability/hosts Get the status of the cluster X
/highAvailability/zkstatus Get zookeeper cluster status X
​/highAvailability/highAvailability Retrieve the list of preferences X X
​/highAvailability/stepdown Request the primary node to step down X
NETCONF
/netconf/profiles Get all Profiles X X X X
/netconf/netconfCollection/liveNetwork Create a new collection. X
/netconf/netconfCollection/liveNetwork/​{id} Get the status of a collection job X
TOPOLOGY
/topology List available topologies. X
/topology/​{topology_id} Get topological element X X
​/topology/​{topology_id}​/status Get topological element status X
/topology/​{topology_id}​/status/pce Get PCEP protocol adapter status X
/topology/​{topology_id}​/status/topologyAcquisition Get BGP-LS protocol adapter status X
​/topology/​{topology_id}​/status/pathComputationServer Get path computation server status X
/topology/​{topology_id}​/status/transportTopologyAcquisition Get transport topology acquisition status X
Facilities
​/topology/​{topology_id}​/facilities Get all facilities X
​/topology/​{topology_id}​/facilities/​{facilityIndex} Get info for a single facility X X X X
Links
/topology/​{topology_id}​/links Create and get link info X X
/topology/​{topology_id}​/links/search Search links X
/topology/​{topology_id}​/links/​{linkIndex} Get a single link X X X
​/topology/​{topology_id}​/links/​{linkIndex}​/history Get link event history X
Nodes
​/topology/​{topology_id}​/nodes Manage nodes X X
/topology/​{topology_id}​/nodes/search Search nodes X
​/topology/​{topology_id}​/nodes/​{nodeIndex} Manage single node details X X X
/topology/​{topology_id}​/nodes/​{nodeIndex}​/history Get node event history X
P2MP
/topology/​{topology_id}​/p2mp Get all P2MP groups X
/topology/​{topology_id}​/p2mp/​{p2mpGroupIndex} Get specified P2MP group X
/topology/​{topology_id}​/p2mp/​{p2mpGroupIndex}​/​{lspIndex} Get a given P2MP Branch X
Path computation
​/topology/​{topology_id}​/pathComputation Request a path computation X
​TE-LSPs
​/topology/​{topology_id}​/te-lsps Manage TE-LSPs X X
​/topology/​{topology_id}​/te-lsps/search Search LSPs X
/topology/​{topology_id}​/te-lsps/​{lspIndex} Get or update a single TE-LSP X X X
/topology/​{topology_id}​/te-lsps/bulk Create a list of TE-LSPs X X X
/topology/​{topology_id}​/te-lsps/​{lspIndex}​/history Retrieve the LSP event history X
Transport controllers
/transportControllers Manage transport controllers X X
/transportControllers/​{transportControllerIndex} Get or update a transport controller configuration X X X X
Transport controller groups
​/transportControllerGroups Manage transport controller device groups X X X
/transportControllerGroups/​{transportControllerGroupName} Manage specified transport controller device group X X X X

Resource Notifications

You can create a socket interface for receiving asynchronous event notifications on the following objects, using the restNotifications-v2 namespace.


socketIO = SocketIO(serverURL, 8443,verify=False,headers= headers)
ns = socketIO.define(NSNotificationNamespace, '/restNotifications-v2')
            

Notification events:

  • Add

  • Remove

  • Update

Object Definition Description
Node topology_v2.json#/definitions/nodeNotification Node event notification
Link topology_v2.json#/definitions/linkNotification Link event notification
LSP topology_v2.json#/definitions/lspNotification LSP event notification
P2MP group topology_v2.json#/definitions/p2mpGroupNotification P2MP group event notification
Facility/SRLG topology_v2.json#/definitions/facilityNotification Facility/SRLG event notification
High availability topology_v2.json#/definitions/haHostNotification Node state event notification

NorthStar Architecture

NorthStar Controller multilayer controller is a powerful and flexible traffic engineering solution that dynamically interacts with transport/optical controllers to reroute IP/MPLS flows.

As shown in diagram below, NorthStar receives input from three main sources:

  • Real-time information about topology, traffic statistics and LSP status from the network

  • Requests from a human operator using the GUI

  • Requests from a northbound REST API

Based on these inputs, the controller makes decisions about the placement of LSPs in the network, and can create, modify, and tear down LSPs as needed. Because the controller is coupled to the live network, it can react immediately to changing network conditions.

Key protocol ingredients that give the NorthStar Controller visibility and control of the live network are:

  • BGP-LS, which provides visibility of the network topology and the TE attributes of a link, such as metric, bandwidth, and admin groups.

  • The Path Computation Element Protocol (PCEP), whicrestNotifications-v2 eventsh propagates the status and attributes of network LSPs. Furthermore, PCEP permits the controller to modify the paths of LSPs and to create LSPs.

The figure shows the packet-optical control plane integration:

Features

Dynamic topology acquisition Use the IS-IS, OSPF, and BGP-LS routing protocols to get real-time topology updates.

Label-switched path reporting Label edge routers (LERs) use PCEP reports to report PCC-controlled, PCC-delegated, and PCE-initiated LSPs to the NorthStar Controller. To provide dynamic path provisioning, every ingress LER must be configured as a Path Computation Client (PCC).

LSP provisioning Create LSPs using the NorthStar Controller or update LSPs delegated to the NorthStar Controller.

High availability (active/standby) The high availability (HA) implementation provides an active/standby solution, where the active node in the cluster runs the active NorthStar PCE, Toposerver, Path Computation, and REST components, and the standby nodes run those processes needed only to maintain database and BGP-LS connectivity unless the active node fails. HA is an optional, licensed, feature.

NorthStar Network Elements

The NorthStar Controller is an active stateful PCE that utilizes PCEP to get LSP state for centralized control and optimization of network resources to compute network paths or routes based on a network graph and to apply computational constraints.

The figure shows the active stateful PCE architecture:

The PCE entity in the NorthStar Controller calculates paths in the network on behalf of the PCCs, which request path computation services. The PCCs receive and apply the paths in the network.  A PCC is a client application that requests the PCE perform path computations for the PCC external LSPs. The PCE can actively install and modify network paths based on user input using the GUI or by using input from the RESTful API northbound interface. The PCE provides these functions:

  • On-line and off-line LSP path computation

  • LSP reroute triggering when the network needs to be re-optimized

  • LSP bandwidth modification when an application demands an increase in bandwidth

  • Other LSP attributes modification, such as explicit route object (ERO), setup priority, and hold priority

The PCEP enables communication between a PCC and the NorthStar Controller to learn about the network and LSP path state, and to communicate with the PCCs.  Through the PCEP, every PCC informs the PCE server (NorthStar Controller), asynchronously, about the state of LSPs, including LSP operational state, admin state, and protection in-use events. PCEP functions include:

  • LSP tunnel state synchronization between a PCC and a stateful PCE. When an active PCE connection is detected, a PCC synchronizes an LSP state with the PCE. The PCEP enables a fast and timely synchronization of the LSP state to the PCE.

  • Delegation of control over LSP tunnels to a stateful PCE. An active stateful PCE controls one or more LSP attributes for computing paths, such as bandwidth, path (ERO), and priority (setup and hold). The PCEP enables such delegation of LSPs.

  • Stateful PCE control of timing and sequence of path computations within and across PCEP sessions. An active stateful PCE modifies one or more LSP attributes, such as bandwidth, path (ERO), and priority (setup and hold). The PCEP communicates these new LSP attributes from the PCE to the PCC, after which the PCC re-signals the LSP in the specified path.

NorthStar Implementation

The NorthStar Controller is a software application running on a suitable third-party x86 hardware platform. The PCC runs in a new Junos OS daemon called the Path Computation Client Process (PCCD), which interacts with the PCE and with the Routing Protocol Process (RPD) through a Junos OS IPC mechanism. The figure shows the PCE, PCCD, and RPD interaction:

The stateless PCCD does not keep state information other than current outstanding requests, and does not remember established LSP state.

The PCCD requests the state after the response PCE responds and forwards the response to the RPD. Because the PCCD is stateless, the RPD needs to communicate only with the PCCD when the LSP is first created. After the RPD receives the results from the PCCD, the results are stored, including across RPD restarts, and the RPD does not need to communicate with the PCCD until the LSP is rerouted and the LSP configuration changes, or the LSP fails.

A TCP-based PCEP session connects a PCC to an external PCE. The PCC starts the PCEP session and stays connected to the PCE for the duration of the PCEP session.  During the PCEP session, the PCC requests LSP parameters from the stateful PCE. When receiving one or more LSP parameters from the PCE, the PCC re-signals the TE LSP.

LSP state update and provisioning depend on the TCP/PCEP connection state. After the PCEP session terminates, the underlying TCP connection is closed immediately and the PCC attempts to reestablish the PCEP session. If the TCP connection terminates due to connection or PCC failure, the NorthStar Controller waits approximately 60 seconds for PCC reconnection before removing the LSP state.

Juniper Networks Junos operating system integrates the PCC, permitting the NorthStar Controller to get the LSP state from a Juniper platform running a suitable Junos OS release, and third-party routers supporting RFC5440. NorthStar Controller peers with the network to get a global view of the network topology, typically using BGP-LS by importing the traffic engineering database (TED). Alternatively, it can also learn the network topology using OSPF/ISIS-TE. The combined visibility of the network topology and complete LSP state in the network permits the NorthStar Controller to provide optimized TE, which enables a diverse range of use cases such as diverse path computation, premium path computation, maintenance mode rerouting, bandwidth scheduling/calendaring, and bin packing or network defragmentation. The interface between the NorthStar Controller and one or more transport layer SDN controllers extends NorthStar Controller visibility into the transport layer and addresses multilayer coordination and optimization use cases.

NorthStar Controller User Interfaces

The NorthStar Controller provides GUI and API interfaces for monitoring and managing controller resources.

This document focus on the NorthStar API.

Graphical User Interface

See the NorthStar Controller Web User Interface Guide for information about how to use the UI.

Application Program Interface (API)

RESTful APIs make it easy for external applications and operations/business support systems (OSS/ BSS) to interactively provision resources and orchestrate services across data centers and network elements.

The API consists of a RESTful API, for sending and receiving information to/from the NorthStar Controller under program control, and a notification API, for receiving asynchronous events from the NorthStar Controller.

RESTful API

Use RESTful API to programmatically interact with the NorthStar Controller, to create custom applications that integrate NorthStar Controller real-time topology, traffic statistics, and LSP management resources.

The API uses resource-specific URLs, HTTP verbs, and HTTP response codes to indicate API errors.

To operate securely with the NorthStar Controller from a client web application, the API depends on OAUth2.0 authentication and all API requests must be made over HTTPS. Requests over HTTP without authentication fail.

Responses are in JavaScript Object Notation (JSON) format, by default, supporting UTF-8 and date-time information in ISO8601 format.

Event Notification

The REST API notifications feature uses socket.io interface to notify about event notification to third party application.

The notification interface uses the same token mechanism as the other REST APIs, so for the third party application to receive all events notifications, the third party application needs to be authenticated first. This is to ensure access is secure and protected.

All notification follow a very similar schema to identify which kind of notification the JSON object refers to, the object content follows the same schema as the object the user or third party application can access using the REST API.

References

Standards and Definitions

Architectural Styles and the Design of Network-based Software Architectures, PhD. Dissertation, R. Fielding, 2000 RFC5545
  • http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm

  • Architecture of the World Wide Web, First Edition, W3C Working Draft, I. Jacobs, 9 December 2003
  • https://www.w3.org/TR/2003/WD-webarch-20031209/

  • The OAuth 2.0 Authorization Framework
  • https://tools.ietf.org/html/rfc6749

  • socket.io
  • http://socket.io/

  • ISO 8601:2004 Representation of dates and times
  • http://www.iso.org/iso/catalogue_detail?csnumber=40874

  • RESTCONF Protocol
  • https://tools.ietf.org/html/draft-ietf-netconf-restconf-18

  • RFC 1945 Hypertext Transfer Protocol -- HTTP/1.0
  • https://tools.ietf.org/html/rfc1945

  • RFC 2396 Uniform Resource Identifiers (URI): Generic Syntax
  • https://www.ietf.org/rfc/rfc2396.txt

  • RFC 4627 The application/json Media Type for JavaScript Object Notation (JSON)
  • https://www.ietf.org/rfc/rfc4627.txt

  • RFC 5440 Path Computation Element (PCE) Communication Protocol (PCEP)
  • https://tools.ietf.org/html/rfc5440

  • RFC 5545, section 3.3.5 Internet Calendaring and Scheduling Core Object Specification
  • https://tools.ietf.org/html/rfc5545

  • RFC 6020 YANG
  • https://tools.ietf.org/html/rfc6020

  • RFC 6241 Network Configuration Protocol (NETCONF)
  • https://tools.ietf.org/html/rfc6241

  • RFC 7159 The JavaScript Object Notation (JSON) Data Interchange Format
  • https://tools.ietf.org/html/rfc7159

  • Acronyms and Abbreviations

    API

  • Application Program Interface

  • BGP-LS
  • Border Gateway Protocol for Link-State

  • CLI
  • Command-line Interface

  • CSV
  • Comma-Separated Values

  • ERO
  • Explicit Route Object

  • GUI
  • Graphical User Interface

  • HA
  • High Availability

  • HTTP
  • HyperText Transfer Protocol

  • HTTPS
  • HyperText Transfer Protocol Secure

  • IGP
  • Interior Gateway Protocol

  • IP
  • Internet Protocol

  • IPC
  • Inter-process Communication

  • IS-IS
  • Intermediate System to Intermediate System

  • JET
  • Junos Extension Toolkit

  • JSON
  • Java Script Object Notification

  • LER
  • Label Edge Router

  • LSP
  • Label Switched Path

  • MPLS
  • Multiprotocol Label Switching

  • OSPF
  • Open Shortest Path First

  • P2MP
  • Point to Multipoint

  • PCC
  • Path Computation Client

  • PCCD
  • Path Computation Client Process

  • PCE
  • Path Computation Element

  • PCEP
  • Path Computation Element Protocol

  • REST
  • Representational State Transfer

  • RPD
  • Routing Protocol Process

  • SDN
  • Software-defined Networking

  • SRLG
  • Shared Risk Link Groups

  • SSH
  • Secure Shell

  • TCP
  • Transmission Control Protocol

  • TE
  • Traffic Engineering

  • TE-LSP
  • Traffic Engineering Label Switched Path

  • TED
  • Traffic Engineering Database

  • UI
  • User Interface

  • URI
  • Uniform Resource Identifier

  • URL
  • Uniform Resource Locator

  • VRR
  • Virtual Route Reflector

  • WAN
  • Wide area Network

  • NorthStar API Basics

    This section describes REST interface concepts developers need to know to use the the NorthStar API, including authentication, request and response data formats, programming conventions, and error handling.

    Refer to the NorthStar API reference documentation to learn about endpoint-specific details.

    Versions

    Beginning with the NorthStar Controller version 2.1 release, the NorthStar API implements the /v2 endpoint: https://northstar.<host>.net:<port>/NorthStar/API/v2.

    The v2 API enhances the v1 API by adding support for:

    • P2MP groups

    • SRLG/facilities

    • Path computation

    • Event notification, to push updates to REST API clients

    Version v2 also permits separate names and IDs for nodes and links, where you have control over names but IDs are under NorthStar control. ???What does this mean???

    Version v2 is not backwards-compatible with version v1 resources.

    The descriptions and examples in this document refer to API version v2.

    Schema

    All data are accessed using HTTPS with request and response data formatted as JSON data, conforming to the JSON-Schema specification.

    The main schema is defined in topology_v2.json, which includes references to JSON data structures for individual resources and for notifications.

    Common data types are described in common.json.

    REST Methods

    The NorthStar API provides methods for interacting with NorthStar resources. By REST convention, resources are equivalent to nouns and the methods are verbs, because they describe the action to be performed on the resource. The NorthStar API supports the methods listed, although, not all resources support all of these methods:

    • GET Get the list of resources for the URI, or information about a specific resource.

    • POST Create a resource.

    • PUT Update a resource.

    • DELETE Remove a resource.

    The API reference states which resources support which methods.

    Resource URL Format

    Resources are identified by a Uniform Resource Identifier (URI). To access a resource using one of the HTTP methods, give the method the location (URL) or the resource. Construct the resource location and identifier from server location, API identifier, API version, context, and the resource URI.

    Server location: <host>:<port>

    API identifier and version: NorthStar/API/v2

    Context: tenant/1/topology/1

    Tenant ID and topology ID values always have a value of 1.

    URI and parameters defined for the resource: <URI>[[?parameter] ...]

    For example, to list all nodes, send a GET request with the URL:

    https://northstar.example.net:8443/NorthStar/API/v2/tenant/1/topology/1/nodes

    For most resources, data are sent to the resource in the request body instead of as URL parameters. Generally, only NorthStar API /search and /history URIs use URL parameters to specify filtering options.

    Requests and Responses

    The NorthStar REST API uses HTTP methods to send and receive JSON content. You can use tools or programming language REST libraries of your choice to make REST requests and receive responses. (Throughout this document we use the cURL tool to demonstrate making REST requests.)

    The URL associated with a resource might include parameters as part of the URL. In the NorthStar API, URL parameters generally only apply to /search and /history endpoints.

    The request header is used to specify control information, such as the data format and access tokens.

    An HTTP request includes a request header and a request body. For endpoints involving data exchange, the data are transmitted in the request body.

    Request

    Send a POST, PUT, GET, or DELETE request to the NorthStar server.

    • The request is sent with a URL address, which identifies the resource for a specific tenant ID and topology ID, as applicable. Specify parameters in the URL, as supported by the endpoint.

    • You must have previously authenticated with the NorthStar Controller and set your access token in the request header:

      Authorization: 'Bearer <accessToken>'

    • Include required JSON-formatted data in the request body, as specified in the API reference. If data are passed with the request, specify the JSON content type in the request header:

      Content-Type: application/json

    Example (GET request):

    curl -H "Authorization: Bearer zRApShiOxoCcBiFGPRhISKAbaUACWQBRqMPmaq40/NU=" -H "Content-Type: application/json" northstar.example.net:8443/NorthStar/API/v2/tenant/1/topology/1/nodes

    Example (POST request):

    curl -X POST -H "Authorization: Bearer zRApShiOxoCcBiFGPRhISKAbaUACWQBRqMPmaq40/NU=" -H "Content-Type: application/json" -d '{"name":"testNode", "topologyIndex":"1", "topoObjectType":"node"}' northstar.example.net:8443/NorthStar/API/v2/tenant/1/topology/1/nodes

    Response

    The server returns an HTTP return status and error message, if an error occurred. On a successful request, data associated with the endpoint is also returned in the response body, in JSON format.

    Naming Conventions

    Most URIs include tenant and topology IDs, tenant_id, topology_id, to provide resource context:

    Context Attribute Type Description
    tenant_id integer A unique tenant or account identifier. The value should always be set to 1.
    topology_id integer A unique topology identifier. The value should always be set to 1.

    Common Definitions

    The common.json file defines common URI literals used by the API.

    Here are some of the more common basic literal definitions:

    Literal Type regex/enum/constant Description
    name string ^[a-zA-Z0-9_\\-\\.\\:]*$ Object name. (Space and comma characters not permitted.)
    adminStatus string Up,Down,Unknown,Planned ???Need definitions??? Interface admin status.
    operStatus string Up,Down,Unknown,Empty ???Need definitions??? Operational status.
    startTime string See dateTime Formatted start time.
    endTime string See dateTime Formatted end time.
    recurrence FREQ DAILY only Frequency specification
    dateTime string See RFC 5545, section 3.3.5 Formatted time.
    username string Node event notification.
    password string Write-only. Not returned with GET response.
    token string Link event notification.
    notificationAction string add, remove, update Action on the object being notified.

    HTTP Status Codes

    All HTTP requests are acknowledged with a status response. On a successful response the data are also included for those methods and endpoints that return data.

    A status code in the 200 range indicates a successful operation. Other status codes indicate an error condition, and it is up to the client to handle the error. The NorthStar API defines select HTTP status codes to mean:

    Status Code Methods Description
    200 All methods Request successfully processed.
    201 POST, PUT, DELETE Resource successfully created or deleted.
    202 PUT Request accepted for processing.
    204 DELETE Request successfully processed without returning data.
    400 All methods Invalid request error. Can occur on any HTTP request.
    401 All methods Authentication failed error. Can occur on any HTTP request.
    403 All methods Valid request rejected by server error. Can occur on any HTTP request.
    404 GET Resource not found error; invalid ID or deleted.
    405 All methods Invalid method for resource error. Can occur on any HTTP request.
    409 All methods Request timeout error.
    413 All methods Payload too large error. Can occur on any HTTP request.
    415 POST, PUT Unsupported media type error.
    422 POST, PUT Valid request but cannot be processed error.
    503 All methods Resource unavailable error. Can occur on any HTTP request.

    Connecting

    Access the NorthStar server using port 8091 or secure port 8443. The base URLs are:

    http://<hostname>:8091/NorthStar/API/

    https://<hostname>:8443/NorthStar/API/

    Authentication

    Every API request must be authenticated by securely validate your identity before acting on the request. This is done by including an authorization field in the request header. To further secure the authorization field, you must use the HTTPS protocol, SSL over HTTP, for every request. The NorthStar API uses the OAuth2 protocol for authentication.

    OAuth2 is an open protocol that validates you have permission to access a resource without requiring your application to store a username and password. Your client sends an authentication request to the NorthStar Controller, which forwards it to the external LDAP server. When the LDAP server accepts the request, NorthStar queries the user profile for authorization and returns an authentication token to your client. You use this authentication token in all subsequent REST requests.

    The steps for securely accessing NorthStar resources are:

    1. Set up your NorthStar username:password credentials.

    2. Use your username:password credentials to get an OAuth2 token, using the /oauth2/token endpoint. For example:

      curl -u ${username}:${password} -X POST -k -H "Content-Type: application/json" -d '{"grant_type":"password","username":"'${username}'","password":"'${password}'"}' https://northstar.example.net:8443/oauth2/token

      The request header specifies a grant type, which is password, followed by your username and password credentials.

      On successful authorization, the authentication server responds with an access token and the token type Bearer, as JSON data. For example:

      { "access_token": "zRApShiOxoCcBiFGPRhISKAbaUACWQBRqMPmaq40/NU=", "token_type": "Bearer"}

    3. Include the access token in the header of all subsequent API requests:

      Authorization: Bearer zRApShiOxoCcBiFGPRhISKAbaUACWQBRqMPmaq40/NU=

    The NorthStar server returns a 401 HTTP status code, if your API request fails authentication.

    Asynchronous Notification

    NorthStar can use asynchronous notification to signal your application of the occurrence of an event on these resources:

    • Node

    • Link

    • LSP

    • P2MP

    • Facility

    • High availability

    A socket.io interface is used on which your application can pend for event notifications. The socket.io namespace used is /restNotifications-v2. Events that trigger notification are:

    • add

    • remove

    • update

    Unless noted, the new value of an object, or the index of the object on object removal, is also sent in the event.

    This Python example, using the requests HTTP library, shows how to set up your application to listen for events:

    
    #!/usr/bin/env python
    from socketIO_client import SocketIO, BaseNamespace
    import requests,json,sys
    
    serverURL = 'https://northstar.example.net'
    username = 'user'
    password = 'password'
    
    class NSNotificationNamespace(BaseNamespace):
        def on_connect(self):
            print('Connected to %s:8443/restNotifications-v2'%serverURL)
        def on_event(key,name,data):
            print "NorthStar Event: %r,data:%r"%(name,json.dumps(data))
    
    payload = {'grant_type': 'password','username': username,'password': password}
    r = requests.post(serverURL + ':8443/oauth2/token',data=payload,verify=False,auth=(username, password))
    data =r.json()
    if "token_type" not in data or "access_token" not in data:
        print "Error: Invalid credentials"
        sys.exit(1)
    
    headers= {'Authorization': "{token_type} {access_token}".format(**data)}
    socketIO = SocketIO(serverURL, 8443,verify=False,headers= headers)
    ns = socketIO.define(NSNotificationNamespace, '/restNotifications-v2')
    socketIO.wait()
            

    First, authenticate with the authentication server and use the returned token to establish a socket connection with the resource server. Use the on_event() method to handle the events, as appropriate for your application.

    Tools

    Several tools are available for working with REST and making HTTP requests, including command-line utilities, browser add-ons, and language-specific program libraries.

    Basic examples in this documentation uses the cURL tool because it is commonly available, requires minimal setup, and can execute REST requests from the command line. More complex, multi-step examples are demonstrated using Python scripts using the requests or httplib2 HTTP libraries.

    cURL

    In a command window, enter curl -v to see if the cURL command-line utility is installed on your system. cURL is installed by default on Mac and *nix systems. For Windows, you need to download the applicable version from https://curl.haxx.se/download.html and set your PATH environment variable so cURL can be executed from the command-line. For a description of cURL syntax and command-line options, enter man curl or see https://curl.haxx.se/docs/manpage.html.

    You can now use cURL to send HTTP requests to the server. Before sending a request, you need to authenticate with the authentication server, as described in the Authentication section. Following authenticated, simply include the Bearer authentication code with subsequent requests.

    This example to get the full topology shows how to send an HTTP GET request:

    
    curl -H "Authorization: Bearer zRApShiOxoCcBiFGPRhISKAbaUACWQBRqMPmaq40/NU=" -H "Content-Type: application/json" northstar.example.net:8443/NorthStar/API/v2/tenant/1/topology/1
                    

    This example to set node name, index, and object type shows how to send an HTTP POST request:

    
    curl -X POST -H "Authorization: Bearer zRApShiOxoCcBiFGPRhISKAbaUACWQBRqMPmaq40/NU=" -H "Content-Type: application/json" -d '{"name":"testNode", "topologyIndex":"1", "topoObjectType":"node"}' northstar.example.net:8443/NorthStar/API/v2/tenant/1/topology/1/nodes
                    

    Both examples specify the destination REST endpoint in the URL of the NorthStar server, identified by hostname and port number. Notice how the examples include the required authorization code previously returned by the authentication server: zRApShiOxoCcBiFGPRhISKAbaUACWQBRqMPmaq40/NU=.

    A successful request returns an HTTP status code of 200 and an unsuccessful requests returns a non-200 status code with an error description.

    The -H option defines the HTTP header sent with the request, which includes the authorization code, a JSON-formatted content type, and data associated with the POST request.

    The -X option specifies a non-default request type, which is POST. The default request type is GET.

    REST Clients

    Numerous REST clients are available as browser add-ons, making it easy to sent HTTP requests by providing URL and request body and header formatting assistance, HTTP method selection, and better visualization of returned status and data.

    Popular Chrome REST clients include Postman and Advanced REST Client.

    RESTClient is available for Firefox.

    You can also use the native Postman app, which can be downloaded at: https://www.getpostman.com/apps

    Juniper REST API Explorer

    https://www.juniper.net/documentation/en_US/junos15.1/topics/example/rest-api-explorer-example.html

    JSON Viewers

    All NorthStar API request data are JSON-formatted so it can be helpful to use a JSON viewer, whether to help find formatting errors in transmitted data or to help visualize data returned by the NorthStar server, using syntax highlighting and pretty-print formatting.

    Your text editor and IDE likely have built-in or add-on support for viewing JSON data, and several online JSON viewers are available.

    Development Environment

    Is Juniper development framework for JET Apps using Eclipse IDE relevant?

    Is NorthStar Python Library relevant?

    Using the NorthStar API

    The tutorials in this section give you hands-on experience with the NorthStar API. Progressing from common, basic functions to more involved examples that access a broader range of NorthStar features, these tutorials give you the foundation you need to start developing your own custom applications:

    Authenticate with NorthStar

    Monitor NorthStar Health

    Update LSP Bandwidth

    Create a Full LSP Mesh

    Send Routing Constraints to NorthStar from a Junos Application

    To understand these examples and try them on your computer, you should be familiar with Python and the JSON data format, and how to use HTTP and JSON Python libraries.

    Most HTTP libraries have similar functionality so you can use the library preferred in your environment. These tutorials use the requests or httplib2 libraries. Other HTTP libraries might require you to use other JSON libraries, such as json or simplejson, for earlier Python versions.

    You can run these examples on your local computer, connecting to NorthStar in your Juniper network. You might need to change hostname and port number shown in these examples to that of your NorthStar server. Also, depending on your network configuration, the JSON data returned by the NorthStar server might differ from that shown in these examples.

    Experiment with changes to these examples to get a deeper understanding of NorthStar functionality and how the API works.

    Authenticate with NorthStar

    Before making any NorthStar API request, you must authenticate with the NorthStar server, providing your NorthStar username and password and receiving an access token. This only needs to be done once, after which you include the access token in the header of every API request.

    This example shows you how to authenticate with the NorthStar server. We use the requests Python HTTP library, which includes JSON encode and decode methods. In some environments, you might prefer to also use the json library for encoding and decoding JSON data.

    The example finishes with a PCE status request to verify that you have a valid access token and to give you the status of your NorthStar server.

    Begin by importing the requests library.

    
    #!/usr/bin/env python
    import requests
                

    For now, we hard-code example hostname, username, and user password. Later examples demonstrate how to get these variable from command-line arguments. Change these, if you want to run the example on your system.

    
    nsHost = "northstar.example.net"
    username = "admin"
    password =  "changeme"
                

    Now, we make an API request to the NorthStar server, addressing port 8443 for authentication. You need to format and transmit a header with the request to specify the JSON data format, application/json, and to provide OAuth2 credentials, 'grant_type':<type of authorization>,'username':<your NorthStar username>,'password':<your NorthStar password>. You also need to provide your credentials in the auth parameter to access the NorthStar server.

    
    response = requests.post("https://{server}:8443/oauth2/token".format(server=nsHost),
                 headers={'content-type':'application/json'},json={'grant_type':'password','username':username,'password':password},auth=(username,password))
                

    On a successful authentication, the server returns the access token to be used in subsequent requests. Decode the data for use in subsequent API calls.

    
    authData = response.json()
                

    For OAuth2, the token_type is always Bearer. The access_token long string, such as zRApShiOxoCcBiFGPRhISKAbaUACWQBRqMPmaq40/NU=, which the server uses to validate your requests. To show how to use the token, we make a PCE status request, and decode and print the response.

    
    pceStatusReq = requests.get("http://{server}:8091/NorthStar/API/v1/tenant/1/topology/1/status/pce.format(server=nsHost)",headers="{'Authorization':'"'{token_type} {access_token}'"'.format(**authData)}"")
    
    pceStatusList = pceStatusReq.json()
    print "NorthStar PCE Status: " + (pceStatusList['status'])
                

    Here is the complete listing for the example:

    
    #!/usr/bin/env python
    import requests
    
    nsHost = "northstar.example.net"
    username = "admin"
    password =  "changeme"
    
    authHeaders=None
    
    response = requests.post("https://{server}:8443/oauth2/token".format(server=nsHost),
                 headers={'content-type':'application/json'},json={'grant_type':'password','username':username,'password':password},auth=(username,password))
    
    authData = response.json()
    
    pceStatusReq = requests.get("http://{server}:8091/NorthStar/API/v1/tenant/1/topology/1/status/pce.format(server=nsHost)",headers="{'Authorization':'"'{token_type} {access_token}'"'.format(**authData)}"")
    pceStatusList = pceStatusReq.json()
    print "Northstar PCE Status: " + (pceStatusList['status'])
                

    This basic authentication example, which is also the first step in communicating with the NorthStar server using the API, gives you the foundation needed for running the remaining examples.

    Monitor NorthStar Health

    This example is a simple Python script that demonstrates how to use the NorthStar REST APIs to get a quick snap-shot of NorthStar health and the state of the LSPs in the network entities reported to NorthStar.

    The script generates a report that displays:

    • PCE status

    • Topology Acquisition Status

    • BGP-LS and PCEP session state summary and session list

    • LSPs state summary and list

    REST APIs Used

    • http://<PCS_addr>:8091/NorthStar/API/v1/tenant/1/topology/1/status/pce

    • http://<vRR_addr>:3000/rpc/get-system-information@format=json

    • http://<PCS_addr>:8091/NorthStar/API/v1/tenant/1/topology/1/status/topologyAcquisition

    • http://<vRR_addr>:3000/rpc/get-bgp-summary-information@format=json

    • http://<vRR_addr>:3000/rpc/get-bgp-neighbor-information@format=json

    • http://<PCS_addr>:8091/NorthStar/API/v1/tenant/1/topology/1/te-lsps

    • http://<PCS_addr>:8091/NorthStar/API/v1/tenant/1/topology/1/nodes

    Preparing NorthStar

    To collect BGP-LS session state information,

    mount the package:

    
    root@vrr% /packages/mnt/jroute/mount.post /packages/mnt/jroute
    root@vrr% ln -s /packages/mnt/jroute/web-api /var/chroot/rest-api
                    

    configure rest service:

    
    set system services rest http port 3000
                    

    (optional) include Junos commands:

    
    set system services rest http addresses <vrr address from ‘vmm ip vrr’>
    set system services rest control allowed-sources <ip address of node making REST call>
    set system services rest control connection-limit 100
                    

    Running the Script

    Script arguments specify the information you want display and whether or not to enable Junos vRR for REST services (by default, the Junos vRR REST interface is not enabled):

    • --host (default: 127.0.0.1) The address of NorthStar PCE.

    • --user (default: none)

    • --password (default: none)

    • --vrr (default: none) – this is the address of the NorthStar vRR

    • --vrrUser (default: root)

    • --vrrPassword (default: Embe1mpls)

    • --verbose (default: none)

    To run the script, enter the following on the command line as an example:

    
    python pce-health-statusv2.py --host 10.92.38.38 --vrr 10.92.38.39
                    

    Example Output

    
    S Health Report
    Executed(Date & Time): Wed Jan 27 11:51:41 2016
    
    Northstar PCE Status: PCE is up.
    Northstar Topology Aquisition Status: Connected to NTAD: 11.105.199.2 port: 450
    
    BGP neighbor Count: 5
    BGP neighbor List:
    ------------------
    Peer State
    11.0.0.101 Established
    11.0.0.102 Established
    11.0.0.103 Established
    11.0.0.104 Established
    11.105.199.1 Established
    
    PCEP session count: 5
    PCEP Session List:
    ------------------
    PCEP IP Node
    11.0.0.101 0110.0000.0101
    11.0.0.102 0110.0000.0102
    11.0.0.103 0110.0000.0103
    11.0.0.104 0110.0000.0104
    11.0.0.105 0110.0000.0105
    
    LSP Tunnel list Summary:
    ------------------------
    Total LSP Count: 24
    LSPs in opStatus Active: 23
    LSPs in opStatus Down: 1
    
    LSP Tunnel List:
    ----------------
    lspName from-address to-address controlType opStatus
    Always_Down_LSP 11.0.0.101 11.0.0.254 PCC Down
    LP_101_103 11.0.0.101 11.0.0.103 Delegated Active
    LSP_VMX101_VMX102 11.0.0.101 11.0.0.102 Delegated Active
    LSP_VMX101_VMX103 11.0.0.101 11.0.0.103 Delegated Active
    LSP_VMX101_VMX104 11.0.0.101 11.0.0.104 Delegated Active
    NLP_101_103 11.0.0.101 11.0.0.103 Delegated Active
    XX_101_103 11.0.0.101 11.0.0.103 Delegated Active
    vmx101-vmx107-autobw1 11.0.0.101 11.0.0.107 Delegated Active
    LSP_VMX102_VMX101 11.0.0.102 11.0.0.101 Delegated Active
    LSP_VMX102_VMX103 11.0.0.102 11.0.0.103 Delegated Active
    LSP_VMX102_VMX104 11.0.0.102 11.0.0.104 Delegated Active
    LP_VMX103_VMX101 11.0.0.103 11.0.0.101 Delegated Active
    LSP_VMX103_VMX101 11.0.0.103 11.0.0.101 Delegated Active
    LSP_VMX103_VMX102 11.0.0.103 11.0.0.102 Delegated Active
    LSP_VMX103_VMX104 11.0.0.103 11.0.0.104 Delegated Active
    NLP_VMX103_VMX101 11.0.0.103 11.0.0.101 Delegated Active
    XX_VMX103_VMX101 11.0.0.103 11.0.0.101 Delegated Active
    LSP_VMX104_VMX101 11.0.0.104 11.0.0.101 Delegated Active
    LSP_VMX104_VMX102 11.0.0.104 11.0.0.102 Delegated Active
    LSP_VMX104_VMX103 11.0.0.104 11.0.0.103 Delegated Active
    DiverseA-102-104 11.0.0.102 11.0.0.104 PCEInitiated Active
    DiverseB-102-104 11.0.0.102 11.0.0.104 PCEInitiated Act
                    

    Example Source Code

    
    #!/usr/bin/env python
    import requests
    import time
    import json
    import re
    # For parsing options
    import optparse
    parser = optparse.OptionParser(description="This programms retrieve the link information and based on a configuration file configure the access links")
    parser.add_option("", "--host", dest="host",help="specify the REST Server to connect to, default 127.0.0.1 ",default="127.0.0.1")
    parser.add_option("", "--user", dest="user",help="The username for REST authentication",default=None)
    parser.add_option("", "--password", dest="password",help="The password for REST authentication ",default=None)
    parser.add_option("", "--vrr", dest="vrr",help="IP address of BGP-LS Topology",default=None)
    parser.add_option("", "--vrrUser", dest="vrrUser",help="The username for REST VRR authentication",default='root')
    parser.add_option("", "--vrrPassword", dest="vrrPassword",help="The password for REST REST authentication",default='Embe1mpls')
    
    parser.add_option("-v", "--verbose", dest="verbose",action="store_true",help="be verbose")
    (options, args) = parser.parse_args()
    
    authHeaders=None
    
    session=requests.Session()
    if (None is not options.user) and (None is not options.password):
    
        x=session.request('POST', "https://{server}:8443/oauth2/token".format(server=options.host),
                          verify=False,headers={'content-type':'application/json'},json={'grant_type':'password','username':options.user,'password':options.password},auth=(options.user,options.password))
        j=json.loads(x.text)
        authHeaders={'Authorization':"{token_type} {access_token}".format(**j)}
    
        # Base NS url for all the requests
        url="http://{server}:8091/NorthStar/API/v1/tenant/1/topology/1".format(server=options.host)
    
        #
        # Small helper to retrieve a key (or a list of key) in the form a/b/c
        #
        def getValue(obj,key,default=None):
          if isinstance(key,list):
              for k in key:
                  v=getValue(obj,k)
                  if v is not None:
                      return v
              return default
          for ki in re.split("[/|]",key):
              obj=obj.get(ki)
              if obj is None:
                  return default
          if obj is not None:
            return obj
          return default
    
        print "\n"
        print "Northstar Health Report"
        print "Executed(Date & Time): " + time.strftime("%c")
        print "\n"
    
        # Make a REST call to Northstar to get the PCE status
        # Put the returned data into a list with a json format
        # Parse the list for the status field
    
        pce_status_req = session.get(url+"/status/pce",headers=authHeaders)
        pce_status_list = pce_status_req.json()
        print "Northstar PCE Status: " + (pce_status_list['status'])
    
        # Make a REST call to Northstar to get Topo Acquisition status
        # Put the returned data into a list with a json format
        # Parse the list for the status field
    
        ntad_status = session.get(url+"/status/topologyAcquisition",headers=authHeaders)
        ntad_status_list = ntad_status.json()
        print "Northstar Topology Aquisition Status: " + ntad_status_list['status']
        print "\n"
    
        # Make a REST call to the vRR with authentication header
        # Put the returned data into a list with a json format
        # parse the list for the BGP peer data & peer-state data fields
        # Print the neighbor count and list each neighbor & its session state
        if options.vrr is not None:
            vrr_status = session.get("http://{vrr}:3000/rpc/get-bgp-summary-information@format=json".format(vrr=options.vrr), auth=(options.vrrUser, options.vrrPassword))
            vrr_status_list = vrr_status.json()
    
            i = int(vrr_status_list['bgp-information'][0]['peer-count'][0]['data'])
            peer_count = (vrr_status_list['bgp-information'][0]['peer-count'][0]['data'])
    
            print "BGP neighbor Count: " + peer_count
            print "BGP neighbor List:"
            print "------------------"
            print "  Peer                   State"
    
            for i in range (0, i):
                print  "  " + (vrr_status_list['bgp-information'][0]['bgp-peer'][i]['peer-address'][0]['data']) + "\t\t" + (vrr_status_list['bgp-information'][0]['bgp-peer'][i]['peer-state'][0]['data'])
            print "\n"
    
        nodes_list=session.get(url+"/nodes",headers=authHeaders)
        nodes_json = nodes_list.json()
        # There is no filter server side, so we need to do it
        nodesWithPCEP=filter(lambda x: ("protocols" in x) and ("PCEP" in x["protocols"]), nodes_json)
        print "PCEP session count: {:d}".format(len(nodesWithPCEP))
        print "PCEP Session List:"
        print "------------------"
        print "  PCEP IP\tNode\n"
        for node in nodesWithPCEP:
            print "{}\t{}".format(getValue(node,"protocols/PCEP/pccAddress"),getValue(node,['hostName',"name"]))
    
        print "\n"
    
        # Summary of LSPs (total, Up, Down) & count each
    
        lsp_list_req = session.get(url+'/te-lsps',headers=authHeaders)
        lsp_list_all = lsp_list_req.json()
        lsp_list_json = json.dumps(lsp_list_all)
    
        lsp_list_down   = filter(lambda x: x.get("operationalStatus") == "Down",lsp_list_all)
        lsp_list_active = filter(lambda x: x.get("operationalStatus") == "Active",lsp_list_all)
    
        print "LSP Tunnel list Summary:"
        print "------------------------"
        print "  Total LSP Count: {:d}".format(len(lsp_list_all))
        print "  LSPs in opStatus Active: {:d}".format(len(lsp_list_active))
        print "  LSPs in opStatus Down: {:d}".format(len(lsp_list_down))
    
        # List each LSP who is in opStatus down
        # formatting: get the longest name
        #
        def max(x,y):
            if y>x:
                return y
            return x
    
        width=reduce(max,map(lambda x:len(x.get('name','')),lsp_list_all),0)
    
        print "\n"
        print "LSP Tunnel List:"
        print "----------------"
        print "   {name:{width}s}\tfrom-address\tto-address\tcontrolType\toperationalStatus".format(width=width,name='lspName')
        print "\n"
        for l in lsp_list_all:
            print "   {name:{width}s}\t{from[address]}\t{to[address]}\t{controlType:12s}\t{operationalStatus}".format(width=width,**l)
                

    Update LSP Bandwidth

    In this example, we look at all NorthStar-managed LSP statistics over the previous two days and, if the 90th-percentile actual bandwidth utilization exceeds the planned bandwidth, we update the planned bandwidth to the computed 90th-percentile value.

    REST APIs Used

    • http://<serverURL>:8443/NorthStar/API/v2/tenant/1/topology/1/te-lsps

    • http://<serverURL>:8443/NorthStar/API/v2/tenant/1/statistics/te-lsps/bulk

    Running the Script

    To run the script, enter the following on the command line:

    
    python changeLSPBwFromStats.py
                    
    In the example, server name, username, and password are hard-coded for simplicity, although, you might choose to add these as command line arguments.
    
    serverURL = 'northstar.example.net'
    username = 'resUser'
    password = 'restPassword'
                    
    Change these in your script as needed for your environment and login credentials.

    Import Packages

    We use the Python requests HTTP library and the json JSON encode and decode package to work with REST request/response body data. We also import sys, re, and numpy to help with computations on the LSP statistical data.

    
    import requests,json,sys,re,numpy
                    

    Authenticate with the NorthStar Server

    As you saw in previous tutorials, the first step is to authenticate with the server to get an access token that you send with subsequent REST requests. For convenience, we use an authenticate function that constructs the authentication part of the request header, and call the function with the server address and our username and password credentials.

    
    def authenticate(serverURL,username, password):
        payload = {'grant_type': 'password','username': username,'password': password}
        r = requests.post('https://'+serverURL + ':8443/oauth2/token',data=payload,verify=False,auth=(username, password))
        data =r.json()
        if "token_type" not in data or "access_token" not in data:
            sys.exit(1)
        return  {'Authorization': "{token_type} {access_token}".format(**data)}
    
    auth_headers = authenticate(serverURL, username, password)
                    

    Set Content Type

    Because all REST data are JSON-formatted, we update the request header with the application/json content type.

    
    auth_headers.update({"Content-Type":"application/json"})
                    

    Get LSP List

    Now we can look for LSPs that exceeded planned bandwidth utilization.

    A GET request to the te-lsps endpoint returns a list of all LSPs and their properties. We iterate through the list and select LSPs whose controller property is NorthStar, building a dictionary of only NorthStar LSPs. For those LSPs, we also save the tunnel name, source IP address, and primary path type.

    
    lspUrl="https://{}:8443/NorthStar/API/v2/tenant/1/topology/1/te-lsps".format(serverURL)
    lsps =  requests.get(lspUrl,verify=False,headers=auth_headers).json()
    
    controlledLSPs =  dict([("%s %s %s"%(x["from"]["address"],x["name"],x.get("pathType","primary")),x) for x in filter(lambda x : (x["controller"] == "NorthStar"),lsps)])
                    

    Get LSP Statistics

    Now that we have a list of LSPs we are interested in, we do a bulk query of those LSPs to get their statistical data.

    We set up the query request to get bandwidth utilization over the previous two days.

    
    query= {
      "endTime": "now",
      "aggregation": "avg",
      "startTime": "now-2d",
      "interval": "1h",
      "lsp": [
      ],
      "counter": ["lsp_stats.bps"]
    }
                    

    We now iterate over the previously created list of NorthStar LSPs and build a list of those LSPs to query in the bulk request, using the LSP server name and address.

    
    for l in controlledLSPs.itervalues():
        query["lsp"].append({"name":l["name"],"node":{'address':l["from"]["address"]}})
                    

    We use the te-lsps/bulk endpoint to POST a request to multiple LSPs, and include the previously constructed query data and request header.

    
    statUrl="https://{}:8443/NorthStar/API/v2/tenant/1/statistics/te-lsps/bulk".format(serverURL)
    stats =  requests.post(statUrl,json=query,verify=False,headers=auth_headers).json()
                    
    The response includes performance data for each LSP.

    Select LSPs That Exceed Planned Bandwidth

    In the response, we iterate throught the returned statistics for each LSP. Using name and IP address to index into the previously built list of controlled LSPs, controlledLSPs, for LSPs that include lspPlannedPathProperties and have bandwidth specified, we:

    • Set the _planned:bwInt temporary variable to the planned bandwidth returned by the query: ["plannedProperties"]["bandwidth"].

    • Set the _lsp_stats.bps temporary variable to the actual bandwidth utilization returned by the query: lsp_stats.bps.

    • Calculate 90 percentile value of the actual bandwidth utilization.

    
    for st in stats.itervalues():
        key = "%s %s %s"%(st["id"]["node"]["address"],st["id"]["name"],"primary")
        l = controlledLSPs.get(key)
        if not l:
            continue
        if "plannedProperties" not in l or "bandwidth" not in l["plannedProperties"]:
            continue
        l["_planned:bwInt"] =  getBWInteger(l["plannedProperties"]["bandwidth"])
        l["_lsp_stats.bps"] = st["lsp_stats.bps"]
        a = numpy.array(l["_lsp_stats.bps"])
        p =int( numpy.percentile(a, 90))
                    

    Build LSP Update Request

    If the 90-percentile bandwidth utilization value is larger than the current planned bandwidth value, we add the LSP to a list of LSPs to be updated. We construct the update request for those LSPs from properties returned in the query, changing only the planned bandwidth value, ["plannedProperties"]["bandwidth"], to the newly computed 90th-percentile value.

    
    lspsToUpdate=[]
    lspOperationalAttributes="controlType initiator tunnelId liveProperties operationalStatus".split()
    lspPlannedOperationalAttributes="calculatedEro operationalStatus lastStatusString correlatedRROHopCount controllerStatus".split()
    
    if p > l["_planned:bwInt"]:
        lsp=dict([(x,l[x]) for x in l.iterkeys() if (x[0] != '_'  and x not in lspOperationalAttributes) ])
        lsp["plannedProperties"] = dict([(x,l["plannedProperties"][x]) for x in l["plannedProperties"].iterkeys() if (x[0] !='_'  and x not in  lspPlannedOperationalAttributes)])
        lsp["plannedProperties"]["bandwidth"] = p
        lspsToUpdate.append(lsp)
                    

    Update LSP That Exceeded Planned Bandwidth Utilization

    Finally, we POST the bulk update request again using the te-lsps/bulk endpoint, which includes the list of LSPs to update, lspsToUpdate, and the new planned bandwidth value.

    
    lspUpdateUrl="https://{}:8443/NorthStar/API/v2/tenant/1/topology/1/te-lsps/bulk".format(serverURL)
    stats =  requests.post(lspUpdateUrl,json=lspsToUpdate,verify=False,headers=auth_headers).json()
                    

    Complete Source Code

    
    #!/usr/bin/env python
    import requests,json,sys,re,numpy
    
    requests.packages.urllib3.disable_warnings()
    
    serverURL = 'northstar.example.net'
    username = 'resUser'
    password = 'restPassword'
    
    def getBWInteger(string):
        if not isinstance(string,basestring):
            return string
        parse = re.split('[mMgGbkK]', string)
        f = float(parse[0])
        if "M" in string or "m" in string or 'Mbps' in string:
            return int(f * 1000000)
        elif "G" in string or 'Gbps' in string or 'g' in string:
            return int(f * 1000000000)
        elif "K" in string or 'Kbps' in string or 'k' in string:
            return int(f * 1000)
        elif "bps" in string:
            return int(f)
        return int(f)
    
    def authenticate(serverURL,username, password):
        payload = {'grant_type': 'password','username': username,'password': password}
        r = requests.post('https://'+serverURL + ':8443/oauth2/token',data=payload,verify=False,auth=(username, password))
        data =r.json()
        if "token_type" not in data or "access_token" not in data:
            print "Error: Invalid credentials"
            sys.exit(1)
        return  {'Authorization': "{token_type} {access_token}".format(**data)}
    
    auth_headers = authenticate(serverURL, username, password)
    auth_headers.update({"Content-Type":"application/json"})
    
    lspUrl="https://{}:8443/NorthStar/API/v2/tenant/1/topology/1/te-lsps".format(serverURL)
    lsps =  requests.get(lspUrl,verify=False,headers=auth_headers).json()
    print  "NS Monitors %d LSP: "%len(lsps)
    
    controlledLSPs =  dict([("%s %s %s"%(x["from"]["address"],x["name"],x.get("pathType","primary")),x) for x in filter(lambda x : (x["controller"] == "NorthStar"),lsps)])
    print  "NS manages %d LSP: "%len(controlledLSPs)
    
    query= {
      "endTime": "now",
      "aggregation": "avg",
      "startTime": "now-2d",
      "interval": "1h",
      "lsp": [
      ],
      "counter": ["lsp_stats.bps"]
    }
    
    for l in controlledLSPs.itervalues():
        query["lsp"].append({"name":l["name"],"node":{'address':l["from"]["address"]}})
    
    statUrl="https://{}:8443/NorthStar/API/v2/tenant/1/statistics/te-lsps/bulk".format(serverURL)
    stats =  requests.post(statUrl,json=query,verify=False,headers=auth_headers).json()
    
    startTime = stats.pop("startTime")
    lspsToUpdate=[]
    lspOperationalAttributes="controlType initiator tunnelId liveProperties operationalStatus".split()
    lspPlannedOperationalAttributes="calculatedEro operationalStatus lastStatusString correlatedRROHopCount controllerStatus".split()
    
    for st in stats.itervalues():
        key = "%s %s %s"%(st["id"]["node"]["address"],st["id"]["name"],"primary")
        l = controlledLSPs.get(key)
        if not l:
            continue
        if "plannedProperties" not in l or "bandwidth" not in l["plannedProperties"]:
            continue
        l["_planned:bwInt"] =  getBWInteger(l["plannedProperties"]["bandwidth"])
        l["_lsp_stats.bps"] = st["lsp_stats.bps"]
        a = numpy.array(l["_lsp_stats.bps"])
        p =int( numpy.percentile(a, 90))
        cross=filter(lambda x : x > l["_planned:bwInt"], l["_lsp_stats.bps"])
        print "LSP %s : threshold crossed %d times, 90 percentile is %d"%(key,len(cross),p)
    
        if p > l["_planned:bwInt"]:
            lsp=dict([(x,l[x]) for x in l.iterkeys() if (x[0] != '_'  and x not in lspOperationalAttributes) ])
            lsp["plannedProperties"] = dict([(x,l["plannedProperties"][x]) for x in l["plannedProperties"].iterkeys() if (x[0] !='_'  and x not in  lspPlannedOperationalAttributes)])
            lsp["plannedProperties"]["bandwidth"] = p
            lspsToUpdate.append(lsp)
    
    if len(lspsToUpdate) > 0:
        print "updates %s"%(json.dumps(lspsToUpdate, sort_keys=True, indent=4, separators=(',', ': ')))
    
        lspUpdateUrl="https://{}:8443/NorthStar/API/v2/tenant/1/topology/1/te-lsps/bulk".format(serverURL)
        stats =  requests.post(lspUpdateUrl,json=lspsToUpdate,verify=False,headers=auth_headers).json()
        print "Updated %d lsps"%(len(stats))
                

    Create a Full LSP Mesh

    This example uses the NorthStar API to dynamically create a full mesh of LSPs among all the PCCs in a network.

    REST APIs Used

    • http://<PCS_addr>:8091/NorthStar/API/v1/tenant/1/topology/1/nodes

    • http://<PCS_addr>:8091/NorthStar/API/v1/tenant/1/topology/1/te-lsps/bulk

    Running the Script

    Script arguments specify the information you want to display and whether or not the Junos vRR is enabled for REST services.

    • --host (default: 127.0.0.1) The address of Northstar PCE.

    • --user (default: none)

    • --password (default: none)

    • --bandwidth (default: 0)

    • --LSPs (default:1) The number of LSPs between two pairs of PCCs in the mesh.

    • --verbose (default: none)

    To run the script, enter in a command window, for example:

    
    python createLspv2.py --host 10.92.38.38 --LSPs 5 --bandwidth 50m
                    

    Example Output

    
    NorthStar LSP Mesh Utility
    Executed(Date & Time): Thu Jan 28 17:59:36 2016
    
    PCEP session count: 4
    PCEP Session List:
    ------------------
    PCEP IP Node
    11.0.0.103 vmx103-11
    11.0.0.104 vmx104-11
    11.0.0.105 vmx105-11
    11.0.0.102 vmx102-11
    
    Do you want to create a full mesh of LSPs between:
    5x50m LSPs from vmx103-11 to vmx104-11
    5x50m LSPs from vmx103-11 to vmx105-11
    5x50m LSPs from vmx103-11 to vmx102-11
    5x50m LSPs from vmx104-11 to vmx103-11
    5x50m LSPs from vmx104-11 to vmx105-11
    5x50m LSPs from vmx104-11 to vmx102-11
    5x50m LSPs from vmx105-11 to vmx103-11
    5x50m LSPs from vmx105-11 to vmx104-11
    5x50m LSPs from vmx105-11 to vmx102-11
    5x50m LSPs from vmx102-11 to vmx103-11
    5x50m LSPs from vmx102-11 to vmx104-11
    5x50m LSPs from vmx102-11 to vmx105-11
    
    Confirm? [yes,no]:yes
    Creating 5x50m LSPs from vmx103-11 to vmx104-11
    ..Created
    Creating 5x50m LSPs from vmx103-11 to vmx105-11
    ..Created
    Creating 5x50m LSPs from vmx103-11 to vmx102-11
    ..Created
    Creating 5x50m LSPs from vmx104-11 to vmx103-11
    ..Created
    Creating 5x50m LSPs from vmx104-11 to vmx105-11
    ..Created
    Creating 5x50m LSPs from vmx104-11 to vmx102-11
    ..Created
    Creating 5x50m LSPs from vmx105-11 to vmx103-11
    ..Created
    Creating 5x50m LSPs from vmx105-11 to vmx104-11
    ..Created
    Creating 5x50m LSPs from vmx105-11 to vmx102-11
    ..Created
    Creating 5x50m LSPs from vmx102-11 to vmx103-11
    ..Created
    Creating 5x50m LSPs from vmx102-11 to vmx104-11
    ..Created
    Creating 5x50m LSPs from vmx102-11 to vmx105-11
    ..Created
    Done
                

    Example Source Code

    
    #!/usr/bin/env python
    import requests
    import time
    import json
    import re
    import itertools
    import sys
    
    # For parsing options
    import optparse
    parser = optparse.OptionParser(description="This programms retrieve the link information and based on a configuration file configure the access links")
    parser.add_option("", "--host", dest="host",help="specify the REST Server to connect to, default 127.0.0.1 ",default="127.0.0.1")
    parser.add_option("", "--user", dest="user",help="The username for REST authentication",default=None)
    parser.add_option("", "--password", dest="password",help="The password for REST authentication ",default=None)
    parser.add_option("", "--bandwidth", dest="bw",help="LSP Bandwidth",default="0")
    parser.add_option("", "--LSPs", dest="lspCount",type='int',help="Number of LSPs to create",default=1)
    
    parser.add_option("-v", "--verbose", dest="verbose",action="store_true",help="be verbose")
    (options, args) = parser.parse_args()
    
    authHeaders=None
    
    session=requests.Session()
    if (None is not options.user) and (None is not options.password):
    
        x=session.request('POST', "https://{server}:8443/oauth2/token".format(server=options.host), verify=False,headers={'content-type':'application/json'},json={'grant_type':'password','username':options.user,'password':options.password},auth=(options.user,options.password))
        j=json.loads(x.text)
        authHeaders={'Authorization':"{token_type} {access_token}".format(**j)}
    
    # Base NS url for all the requests
    url="http://{server}:8091/NorthStar/API/v1/tenant/1/topology/1".format(server=options.host)
    
    #
    # Small helper to retrieve a key (or a list of key) in the form a/b/c
    #
    def getValue(obj,key,default=None):
      if isinstance(key,list):
          for k in key:
              v=getValue(obj,k)
              if v is not None:
                  return v
          return default
      for ki in re.split("[/|]",key):
          obj=obj.get(ki)
          if obj is None:
              return default
      if obj is not None:
        return obj
      return default
    
    nodes_list=session.get(url+"/nodes",headers=authHeaders)
    nodes_json = nodes_list.json()
    # Only get nodes we can use, i.e : Only IP nodes, exclude pseudo node or node without protocol
    nodes_json = filter(lambda x: (x.get("layer",'IP') == 'IP') and not (x.get("pseudoNode",False)) and ('protocols' in x),nodes_json)
    
    # There is no filter server side, so we need to do it
    nodesWithPCEP=dict([ (x['nodeIndex'],x) for x in filter(lambda x: ("protocols" in x) and ("PCEP" in x["protocols"]), nodes_json)])
    #Full mesh of LSPs: create all permutations of nodes, then prune the one starting at non-pcep enable
    nodeMap=dict([(x['nodeIndex'],x) for x in nodes_json])
    
    print "PCEP session count: {:d}".format(len(nodesWithPCEP))
    print "PCEP Session List:"
    print "------------------"
    print "  PCEP IP\tNode\n"
    for node in nodesWithPCEP.itervalues():
        print "{}\t{}".format(node["protocols"]["PCEP"]["pccAddress"],getValue(node,['hostName',"name"]))
    
    # real full mesh,
    fullMesh=itertools.permutations(nodeMap.iterkeys(),2)
    # full mesh PCC <-> PCC
    fullMesh=itertools.permutations(nodesWithPCEP.iterkeys(),2)
    
    def routerId(n):
        return getValue(n,['protocols/ISIS/TERouterId','protocols/OSPF/TERouterId'])
    
    lsps=[]
    
    print "Do you want to create a full mesh of LSPs  between:"
    # permutation is a generator, we can only iterate once
    for (ingress,egress) in filter(lambda x: x[0] in nodesWithPCEP, fullMesh):
        ingressIp = routerId(nodeMap[ingress])
        egressIp  = routerId(nodeMap[egress])
        if not ingressIp or not egressIp:
            continue
        text=" {:d}x{:s}  LSP from  {:s} to {:s} ({:s} -> {:s})".format(options.lspCount,options.bw,getValue(nodeMap[ingress],['hostName',"name"]),getValue(nodeMap[egress],['hostName',"name"]),ingressIp,egressIp)
        print text
    
        lsps.append(("Creating "+text, [
            {'name' : 'AUTO_LSP_{:s}_{:s}_{:d}'.format(getValue(nodeMap[ingress],['hostName',"name"]),getValue(nodeMap[egress],['hostName',"name"]),x), "from":{"topoObjectType": "ipv4","address": ingressIp}, "to":{"topoObjectType":"ipv4","address": egressIp},"plannedProperties": {"bandwidth": "{!s}".format(options.bw),"setupPriority": 7,"holdingPriority": 7}}
    
    for x in range(1,options.lspCount+1)]))
        print "\nThis will create {:d} LSPs".format(options.lspCount*len(lsps))
    
    answer=None
    valid={"yes":True,"true" :True, "y":True,"n":False,"no":False,"false":False}
    while answer is None:
        answer = valid.get(str(raw_input("Confirm? [yes,no]:")).lower(),None)
    if answer:
        # create
        for (text,request) in lsps:
            print text
            response=session.post(url+"/te-lsps/bulk",headers=authHeaders,json=request)
            print "..Created"
        print "Done"
                

    Send Routing Constraints to NorthStar from a Junos Application

    NorthStar offers a number of south bound (SB) interfaces for traditional protocols such as ISIS, OSPFv2, BGP-LS and PCEP. NorthStar also offers a single north bound (NB) interface for retrieving, subscribing to and modifying data in the form of a REST interface. We tend to think of the NB interface as a means for external applications to interface with NorthStar, and the SB interface serves to interface with networking equipment such as routers and switches:

    One of the values of NorthStar, as a centralized PCE, is to provide new methods for routing LSPs across a network, which can be viewed as constraints. Currently, the platform supports several new constraints, including diverse-paths, minimal delay, and maximally protected.

    One limitation of the current PCE architecture is that there is no way of conveying a routing constraint using PCEP. As such, if a customer has deployed LSPs with a constraint, such as a diversity association with another LSP, using manually configuring their ingress routers then the moment orthstar takes control, through LSP delegation, the notion of that constraint is lost. A user can, manually, reprovision LSPs with the constraint using the NorthStar GUI or NB REST API, although this might be difficult for a large deployment.

    This example demonstrates writing a new application for Junos, using the JET framework, that sends routing constraints directly to NorthStar:

    A new Junos daemon, in the form of a JET application, subscribes to MGD configuration change events through the JET Services Daemon (JSD). If the protocols mpls label-switched-path hierarchy is modified the daemon determines if a new routing constraint was added, deleted, or modified and makes a REST call to the NB interface adding the routing constraint to the LSP properties on PCS:

    This a application applies to these scenarios:

    • Adding an LSP using configuration with associated routing constraints When a PCE controlled (delegated) LSP is configured for the first time Junos does not signal the LSP until NorthStar responds with an ERO. This application applies the routing constraints immediately to the routing of the LSP.

    • Adding routing constraints to an existing, PCC controlled, LSP configuration When routing constraints are added to an already signaled LSP, it causes the Jet application to send a REST call to NorthStar updating the attributes of the LSP. NorthStar computes a new path for the LSP at the next periodic reoptimization time. A NorthStar user can also manually trigger an optimization.

    • Modifying routing constraints of a PCE controlled, delegated, LSP When routing constraints are modified on an already signaled LSP, it causes the Jet application to send a REST call to NorthStar updating the attributes of the LSP. NorthStar computes a new path for the LSP at the next periodic reoptimization time. A NorthStar user can also manually trigger an optimization.

    Note: if diversity or symmetry is defined as the routing constraint and the second LSP of the required pair does not yet exist within the diversity/symmetry-group, NorthStar cannot ensure proper routing until the second LSP is added.

    The following routing constraints are supported:

    • diversityGroup: <value>

    • diversityLevel: site(default), link, srlg

    • maxHop: <value>

    • maxDelay: <value>

    • maxCost: <value>

    • symmetryGroup: <value>

    • routingMethod: adminWeight, constant, distance, delay

    • useProtectedLinks: <true> | <false>

    The Junos CLI for adding a routing constraint to an LSP is:

    
    protocols {
        mpls {
            label-switched-path <name> {
                apply-macro pce-routing-attributes {
                    diversity-group "<value>";
                    diversity-constraint "<site> | <link> | <srlg>";
                    symmetry-group "<value>";
                    routing-method "<adminWeight> | <delay> | <constant> | <distance> | <maxProtect>";
                    max-delay "<value>";
                    max-hop "<value>";
                    max-cost "<value>";
                

    JET App Implementation

    This example addresses specific on-box implementation details using JET App. The application reuses the same Python library for basic REST API call handling with minor changes. The main Python file, northstard, implements a Junos daemon instead of using an off-box framework. The northstard file provides the middleware between a JET session and interaction with the NorthStar library:

    The rationale for an on-box JET App is the ability to convey additional information using the NorthStar API and to complement communication that uses standard protocols, such as PCEP and BGP-LS. This enables new use cases for a solution framework, which includes a NorthStar and JET App combination deployed across Juniper devices:

    The implementation is structured as daemonized JET App, using the same jsonpath, requests, urllib2, and NorthStar libraries and is depends on the Juniper development framework for JET Apps. You can develop the application using the JET environment Vagrant VM provided by Juniper engineering, which includes the Eclipse IDE and Junos Extension Toolkit (JET) plugins.

    JET App Structure

    The JET Application is structured around three major components, in the form of Python scripts or libraries:

    NorthStar Baseline Library

    This baseline library reuses https://git.juniper.net/northstar/solutionframework. It includes minor changes for internal logging when compared to the off-box library, but has the same CRUD (Create, Read, Update, Delete) operations, using the same functions. This library is hierarchically designed around the basic CRUD operations to provide higher-level functionality, ensuring reusability and code optimization.

    Main NorthStar Python File

    This main file is the core component for the on0box JET Application. As an analogy, it can be compared with traditional commit-scripts, because it is the main file invoked by JET to constantly listen to specific events. It is the main handler for the JET notification and request-response sessions with a basic multithreading scheme and it properly makes use of the previous application with its exposed functions.

    Dependant Python Libraries

    For basic interaction and implementation of REST API calls, the previous main NorthStar library makes use of standard Python modules, which are explained in more detail, below:

    • jsonpath for data extraction using regular expressions

    • requests for basic JSON data processing and REST API interaction

    • urllib2 for advanced REST API interaction with the NorthStar PCS

    NorthStar Python Library

    The main NorthStar Python library defines initialization, authentication and high-level CRUD (create. read, update and delete) interfaces:

    • def __init__(self,**kvargs) Constructor that sets all generic variables

    • def get_token(self) Issue NorthStar authentication request and get token

    • def get_rest_op(self,path,params=None) Generic NorthStar API GET operation in the context of

    • def post_rest_op(self,path,jsondata) Generic NorthStar API POST operation with Json data attribute

    • def put_rest_op(self,path,jsondata) Generic NorthStar API PUT operation with Json data attribute

    • def del_rest_op(self,path,jsondata) Generic NorthStar API DELETE operation with Json data attribute

    These basic methods provide the foundation for additional methods for advanced data extraction and updates. The figure shows the library layered architecture, with the methods described below:

    GET/Read Methods

    Method Description
    get_topology_index(self) Get active topology index.
    get_nodes(self) Get nodes from active topology.
    get_pcep_active_nodes(self) Extract PCEP enabled routers by issuing a global GET operation to get nodes and parse PEP enabled routers.
    get_node_using_hostname(self,hostname) Search node based on hostname.
    get_node_using_router_id(self,router_id) Get node based on router_id.
    get_node_hostname_using_router_id(self,router_id) Get node hostname based on router_id.
    get_node_name_using_hostname(self,hostname) Get node name in NorthStar based on router_id.
    get_links(self) Get links from active topology.
    get_links_using_node_names(self,node1,node2) Get link based on names from both endpoints.
    get_lsp_using_name_and_source(self,source,lspname) Search LSP based on source and hostname.
    get_pce_initiated_lsps(self) Get all PCE-initiated LSPs.

    POST/Create Methods

    Method Description
    create_node(self,hostname) Create planned node based on hostname (required).
    create_lsp(self,lsp_name,**kvargs) Create LSP based on minimum attributes: source and destination LSP name and IPv4 addresses.
    create_lsp_bulk(self,source,destinations,**kvargs) Create bulk of LSPs based on minimum attributes: source, destinations list, and other attributes, including name regexp.
    construct_lsp_attributes (self,lsp_name,**kvargs) Auxiliary function to construct LSP attributes as a dictionary for POST TE-LSP and TE-LSP bulk operationalStatus.

    PUT/Update Methods

    Method Description
    update_link_attributes(self,link,**kvargs) Update link attributes based on names from both endpoints.
    update_node_attributes(self,node,**kvargs) Update Node attributes.
    update_lsp_attributes (self,oldlsp,**kvargs) Auxiliary function to update LSP attributes as a dictionary for PUT TE-LSP and TE-LSP bulk operationalStatus.
    update_lsp(self,lsp,**kvargs) Update LSP based with no minimum attributes (down to empty update for recalculation).

    DELETE Methods

    Method Description
    delete_lsp(self,lsp_index) Delete LSP based on lspIndex.
    delete_bulk_lsps(self,lsps) Delete input LSPs as a bulk operation.
    reset_topology(self) Reset NorthStar topology.
    delete_all_lsps(self) Delete all NorthStar-initiated LSPs.

    Main JET Application Python File: northstard

    The northstard.py Python file is the main JET application file that interacts with specific events and JET sessions, instantiating NorthStar Python library objects. It is typically invoked in the Junos OS config as the representative file for the JET application and gathers input attributes from the router configuration for object instantiation.

    The file has the following structural elements, which are described below.

    Argument Parsing and JET Session Handling Main Function

    The main() function handles initial arguments for customization. Some attributes are required, while others are optional:

    
    parser = argparse.ArgumentParser(prog=os.path.basename(__file__), description='Northstar JET application')
    # Compulsory attributes identified with required=True
    parser.add_argument("--northstar_address", nargs='?', help="Address of the Northstar server.", type=str, required=True)
    parser.add_argument("--northstar_username", nargs='?', help="Northstar server username.", type=str, required=True)
    parser.add_argument("--northstar_password", nargs='?', help="Northstar server password.", type=str, required=True)
    parser.add_argument("--router_username", nargs='?', help="Router username.", type=str, required=True)
    parser.add_argument("--router_password", nargs='?', help="Router password.", type=str, required=True)
    parser.add_argument("--network_igp", nargs='?', help="IGP used in the network, used for data extraction from TED.", type=str, required=True)
    # Optional attributes, inheriting default values otherwise
    parser.add_argument("--northstar_grant_type", nargs='?', help="Authentication Grant method for Northstar server. (default: %(default)s)", type=str, default=NORTHSTAR_GRANT_TYPE)
    parser.add_argument("--northstar_port", nargs='?', help="Port for Northstar server REST API. (default: %(default)s)", type=str, default=NORTHSTAR_PORT)
    parser.add_argument("--northstar_tenant_id", nargs='?', help="Tenant Id used in Northstar server. (default: %(default)s)", type=str, default=NORTHSTAR_TENANT_ID)
    parser.add_argument("--output", nargs='?', help="Traceoptions output file. (default: %(default)s)", type=str, default=DEFAULT_OUTPUT)
    parser.add_argument("--log_level", nargs='?', help="Traceoptions logging level. (default: %(default)s)", type=str, default=DEFAULT_LOGGING)
                        

    After successfully processing arguments, the function opens notification and request-response JET sessions. The notification session is required for commit notification changes and, therefore, subscribes to commit updates. The request-response session is required for interactive configuration gathering from the router. To optimize resource consumption, a basic multithreading scheme is set up to set the notification session to listen mode:

    
    # Main threading Event
    calling_thread_event = threading.Event()
    global client
    client = JetHandler()
    evHandle = client.OpenNotificationSession(device="localhost",user=args.router_username,password=args.router_password, port=1883)
    
    client.OpenRequestResponseSession(device="localhost",user=args.router_username,password=args.router_password,port=9090)
    cutopic = evHandle.CreateConfigUpdateTopic()
    logger.debug("Subscription for commit notifications")
    evHandle.Subscribe(cutopic,handleCommitNotifications)
    logger.debug("Thread waiting for the events")
    calling_thread_event.wait()
    # This part will never get executed unless an event is triggered by using calling_thread_event.set()
    calling_thread_event.clear()
    logger.debug("Thread finished")
    # Unsubscribe events
    evHandle.Unsubscribe()
    logger.debug("Unsubscription for commit notifications")
    client.CloseRequestResponseSession()
    client.CloseNotificationSession()
                        

    Because the notification session subscribes to configuration updates, a subsequent commit notification handling session is invoked on these events.

    Commit Notification Handling Session

    On every configuration update, this function is called to evaluate the changed hierarchy. If the hierarchy [edit protocols mpls] was notified, the request-response session is triggered to gather the specific LSP stanzas. Otherwise, the change is ignored.

    
    def handleCommitNotifications(message):
        logger.debug('Handling commit notifications for change: ' + str(message))
        try:
            if message['commit-patch']['configuration']['protocols']['mpls']:
                logger.debug("MPLS LSP config changes: " + str(message))
                extractLSPconfig()
        except:
            logger.debug("Config changes not applying to MPLS LSPs")
        return
                        

    LSP Configuration Extraction

    The request-response session gathers the specific LSP configuration:

    
    def extractLSPconfig():
        mgmt_handle = client.GetManagementService()
        conf_query = """
            <get-configuration>
            <configuration junos>
            <protocols> <mpls></mpls>
            </protocols>
            </configuration>
            </get-configuration>
            """
        op_command =
    OperationCommand(conf_query,OperationFormatType.OPERATION_FORMAT_XML,OperationFormatType.OPERATION_FORMAT_JSON)
        result = mgmt_handle.ExecuteOpCommand(op_command)
        parsed_json = json.loads(result.response)
        [...]
                        

    After the LSP configuration is gathered, the stanza is parsed using jsonpath regular expressions to extract the LSP description:

    
    # Check and extract all descriptions from LSP configs using jsonpath
    expr_description = "$..description"
    # Obtain a list of available descriptions
    lsp_descriptions = jsonpath.jsonpath(parsed_json,expr_description)
                        

    Alternatively, the apply-macro pce-routing-attributes stanza is processed:

    
    # Check and extract all apply_macros from LSP configs using jsonpath
    expr_apply_macro = "$..apply-macro"
    # Obtain a list of available apply-macros
    lsp_macros = jsonpath.jsonpath(parsed_json,expr_apply_macro)
                        

    In both cases, the configuration elements are parsed to detect keywords representing attributes. If [protocols mpls label-switched-hath] was modified, the daemonized JET App determines a new routing constraint has been added, deleted or modified, and makes a REST API call for:

    Constraint coded using LSP description:

    
    protocols mpls { label-switched-path <name> {
        description diversityGroup:<dg>;diversityLevel:<dl>;... [...] }}
                        

    or, apply-macro:

    
    protocols mpls {
        label-switched-path <name> {
            apply-macro pce-routing-attributes {
                diversity-group "<value>";
                diversity-constraint "<site> | <link> | <srlg>";
                symmetry-group "<value>";
                routing-method "<adminWeight>"" | <delay> | <constant> | <distance> | <maxProtect>";
                max-delay "<value>";
                max-hop "<value>";
                max-cost "<value>";
                        

    If there is a match in any of these elements, a subsequent function is invoked to start element extraction and NorthStar API calls.

    NorthStar API Calls Driven by Routing Attributes in LSP Description

    In this case, the NorthStar class is instantiated in an object with the original input attributes, because there is guaranteed to be a minimum of one LSP Update through the NorthStar API:

    
    def UpdateLSPsInNorthStarUsingDescription(lsp_json):
    
        # Create northstar handle
        ns =
    northstar.northstar(grant_type=args.northstar_grant_type,username=args.northstar_username,password=args.northstar_password,api_server=args.northstar_address,api_port=args.northstar_port,igp=args.network_igp,tenant_id=args.northstar_tenant_id)
    
        # Authenticate Northstar
        ns.get_token()
    
        # Get default topology index
        ns.get_topology_index()
                        

    After successful authentication and topology index is extracted, this function traverses the LSP descriptions, searching for specific keywords:

    
    # Key_list
    key_list =
    ['diversityGroup','diversityLevel','routingMethod','maxDelay','maxHop','maxCost']
    #Extract LSPs from json file
    for lsp_config in lsp_json['configuration']['protocols']['mpls']['labelswitched-path']:
    […]
                        

    When a match is found, this constructs a dictionary of all key-value pairs and leverages the existing function in the main NorthStar Python library to issue an update API request:

    
    # Parse LSP description with ';' as delimiter
    for item in str(lsp_config['description']).split(';'):
        logger.debug('Extracting LSP description item: ' + item)
        fields = item.split(':')
        if fields[0] == 'diversityGroup':
            lsp_pce_attrib['diversityGroup'] = fields[1]
        if fields[0] == 'diversityLevel':
            lsp_pce_attrib['diversityLevel'] = fields[1]
        if fields[0] == 'routingMethod':
            lsp_pce_attrib['routingMethod'] = fields[1]
        if fields[0] == 'maxDelay':
            lsp_pce_attrib['maxDelay'] = fields[1]
        if fields[0] == 'maxHop':
            lsp_pce_attrib['maxHop'] = fields[1]
        if fields[0] == 'maxCost':
            lsp_pce_attrib['maxCost'] = fields[1]
    logger.debug('Updating LSP ' + lsp_config['name'] + ' with PCE routing attributes: ' + str(lsp_pce_attrib))
    # Update LSP with PCE routing attributes
    ns.update_lsp(old_lsp,**lsp_pce_attrib)
                        

    NorthStar REST API Calls Driven by Routing Attributes in apply-macro

    As in API calls driven by routing attributes in the LSP, the NorthStar class is instantiated in an object with the original input attributes, because there is guaranteed to be a minimum of one LSP update through the API:

    
    def UpdateLSPsInNorthstarUsingMacros(lsp_json):
    
        # Create northstar handle
        ns =
    northstar.northstar(grant_type=args.northstar_grant_type,username=args.northstar_username,password=args.northstar_password,api_server=args.northstar_address,api_port=args.northstar_port,igp=args.network_igp,tenant_id=args.northstar_tenant_id)
    
        # Authenticate Northstar
        ns.get_token()
    
        # Get default topology index
        ns.get_topology_index()
                        

    After successful authentication and the topology index is extracted, this function traverses the LSP configuration, searching for the specific apply-macro:

    
    #Extract LSPs from json file
    for lsp_config in lsp_json['configuration']['protocols']['mpls']['labelswitched-path']:
                        

    When a match is found on the apply-macro name, this constructs a dictionary from all key-value pairs and leverages the existing function in the main NorthStar Python library to issue an update API request:

    
    for item in lsp_macro['data']:
        logger.debug('Extracting LSP apply-macro routing-constraint: ' + str(item))
        if str(item['name']) == 'diversity-group':
            lsp_pce_attrib['diversityGroup'] = item['value']
        if str(item['name']) == 'diversity-constraint':
            lsp_pce_attrib['diversityLevel'] = item['value']
        if str(item['name']) == 'routing-method':
            lsp_pce_attrib['routingMethod'] = item['value']
        if str(item['name']) == 'max-delay':
            lsp_pce_attrib['maxDelay'] = item['value']
        if str(item['name']) == 'max-hop':
            lsp_pce_attrib['maxHop'] = item['value']
        if str(item['name']) == 'max-cost':
            lsp_pce_attrib['maxCost'] = item['value']
    logger.debug('Updating LSP ' + lsp_config['name'] + ' with PCE routing attributes: ' + str(lsp_pce_attrib))
    # Update LSP with PCE routing attributes
    ns.update_lsp(old_lsp,**lsp_pce_attrib)
                        

    Router Baseline Configuration

    The required router configuration stanzas include:

    • Python as scripting language

      
      [edit system]
      scripts {
          language python;
      }
                                  

    • Extension services for request-response (grpc and/or thrift) and notification JET sessions (JET traceoptions enabled for troubleshooting)

      
      [edit system]
      services {
          extension-service {
              request-response {
                  grpc {
                      clear-text;
                  }
                  thrift {
                      clear-text {
                          port 9090;
                      }
                  }
              }
              notification {
                  port 1883;
                  max-connections 20;
                  allow-clients {
                      address 0.0.0.0/0;
                  }
              }
              traceoptions {
                  file jet.log size 1g;
                  flag all;
              }
          }
      }
                                  

    • Commit notification enabling

      
      [edit system]
      commit {
          notification;
      }
                                  

    • JET App extension with concrete input attributes

      
      [edit system]
      extensions {
          extension-service {
              application {
                  file northstard.py {
                      arguments " --router_username regress --router_password MaRtInI
      --northstar_address 192.168.33.4 --northstar_username admin --
      northstar_password adminadmin --network_igp ISIS --output
      /var/log/northstar_jet.log";
                      daemonize;
                      username root;
                  }
              }
          }
      }
                                  

    The JET notification session is required to listen to configuration change commit events and evaluate if a change has occurred within the [edit protocols mpls] stanza.

    The JET request-response session is subsequently required to gather details under [edit protocols mpls] to evaluate PCE routing attributes under a label-switched-path and eventually trigger an update API request with the details. This separate session is required because all PCE routing attributes might not be in the previous commit notification message. The notification message acts as a trigger, if impacting [edit protocols mpls], for the request-response session to evaluate the configuration stanza and eventually issue an API request.

    The JET application is enabled as an extension service in daemonized mode under the root user, so it is continuously running in the background. Input attributes are constant and could, conceptually, be common across the network, and cover mandatory arguments, such as local router username and password, NorthStar credentials, NorthStar PCS host IPv4 address, the network IGP (to properly extract details from the right TED) and a local log file in the router where information is traced (a-latraceoptions: with different logging levels, according to the standard Python logging library).

    Note that there are more considerations for the package implementation in the router:

    • The requests library is a directory, not a standalone file. This library is referrenced and used by the NorthStar library to handle API call details. Currently, even though it is compiled out of the same package, it is a requirement that you explicitly download the Python requests library and upload it to the right /var/db/scripts/jet/ directory:

      
      root@R4> file list /var/db/scripts/jet/
      /var/db/scripts/jet/:
      jsonpath.py@ -> /packages/mnt/northstar/var/db/scripts/jet/jsonpath.py
      northstar.py@ -> /packages/mnt/northstar/var/db/scripts/jet/northstar.py
      northstard.py*
      requests/
      server-extension/
      urllib2.py@ -> /packages/mnt/northstar/var/db/scripts/jet/urllib2.py
                                  

    • If no certificates are used, it is a requirement that you delete the symlink for the main application file, northstard.py, and copy the file from /packages/mnt/northstar/var/db/scripts/jet/ to /var/db/scripts/jet/ directory:

      
      root@R4> file list /var/db/scripts/jet/
      /var/db/scripts/jet/:
      jsonpath.py@ -> /packages/mnt/northstar/var/db/scripts/jet/jsonpath.py
      northstar.py@ -> /packages/mnt/northstar/var/db/scripts/jet/northstar.py
      northstard.py*
      requests/
      server-extension/
      urllib2.py@ -> /packages/mnt/northstar/var/db/scripts/jet/urllib2.py
                                  

    JET Application REST Call Examples

    Diverse LSP Pair

    PE1 configuration for a diversely paired LSP:

    
    protocols {
        mpls {
            label-switched-path pe1-to-pe2 {
                to 1.1.1.2;
                apply-macro pce-routing-attributes {
                    diversity-group 101;
                    diversity-constraint srlg;
                }
                lsp-external-controller pccd;
                        

    REST call JSON data:

    
    {
        "name": ”pe1-to-pe2",
        "from": {"topoObjectType": "ipv4","address": ”1.1.1.1"},
        "to": {"topoObjectType": "ipv4","address": ”1.1.1.2"},
        "design": {
            "diversityLevel":”srlg",
            "diversityGroup":”101”
                        

    PE3 configuration for a diversely paired LSP:

    
    protocols {
        mpls {
            label-switched-path pe3-to-pe4 {
                to 1.1.1.4;
                apply-macro pce-routing-attributes {
                    diversity-group 101;
                    diversity-constraint srlg;
                }
                lsp-external-controller pccd;
                        

    REST call JSON data:

    
    {
        "name": ”pe1-to-pe2",
        "from": {"topoObjectType": "ipv4","address": ”1.1.1.3"},
        "to": {"topoObjectType": "ipv4","address": ”1.1.1.4"},
        "design": {
            "diversityLevel":”srlg",
            "diversityGroup":”101”
                        

    maxDelay Constraint

    PE1 configuration for a LSP with a maximum delay constraint:

    
    protocols {
        mpls {
            label-switched-path pe1-to-pe2 {
            to 1.1.1.2;
            apply-macro pce-routing-attributes {
                max-delay 100;
            }
            lsp-external-controller pccd;
                        

    REST call JSON data:

    
    {
        "name": ”pe1-to-pe2",
        "from": {"topoObjectType": "ipv4","address": ”1.1.1.1"},
        "to": {"topoObjectType": "ipv4","address": ”1.1.1.2"},
        "design": {
            "maxDelay":”100",
                        

    Bidirectional Pair of LSPs

    PE1 configuration:

    
    protocols {
        mpls {
            label-switched-path pe1-to-pe2 {
                to 1.1.1.2;
                apply-macro pce-routing-attributes {
                    symmetry-group 102;
                }
                lsp-external-controller pccd;
                        

    REST call JSON data:

    
    {
        "name": ”pe1-to-pe2",
        "from": {"topoObjectType": "ipv4","address": ”1.1.1.1"},
        "to": {"topoObjectType": "ipv4","address": ”1.1.1.2"},
        "design": {
            "coroutedPairId":”102",
                        

    PE2 configuration:

    
    protocols {
        mpls {
            label-switched-path pe2-to-pe1 {
                to 1.1.1.1;
                apply-macro pce-routing-attributes {
                    symmetry-group 102;
                }
                lsp-external-controller pccd;
                        

    REST call JSON data:

    
    {
        "name": ”pe2-to-pe1",
        "from": {"topoObjectType": "ipv4","address": ”1.1.1.2"},
        "to": {"topoObjectType": "ipv4","address": ”1.1.1.1"},
        19
        "design": {
            "coroutedPairId":”102",
                        

    Routing Methods

    PE1 configuration for a LSP with a delay based routing method:

    
    protocols {
        mpls {
            label-switched-path pe1-to-pe2 {
                to 1.1.1.2;
                apply-macro pce-routing-attributes {
                    routing-method delay;
                }
                lsp-external-controller pccd;
                        

    REST call JSON data:

    
    {
        "name": ”pe1-to-pe2",
        "from": {"topoObjectType": "ipv4","address": ”1.1.1.1"},
        "to": {"topoObjectType": "ipv4","address": ”1.1.1.2"},
        "design": {
            "routingMethod":”delay",