Skip to content
This repository has been archived by the owner on Dec 3, 2021. It is now read-only.

New Lesson - Automating with JET #173

Merged
merged 6 commits into from
Mar 26, 2019
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion images/utility/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ jsnapy
junos-eznc
robotframework
jinja2

paho-mqtt
grpcio
grpcio-tools
# https://github.com/paramiko/paramiko/issues/1369
cryptography==2.4.2
57 changes: 57 additions & 0 deletions lessons/lesson-25/add_firewall_filter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import grpc
import jnx_addr_pb2 as addr
import authentication_service_pb2 as auth
import authentication_service_pb2_grpc
import firewall_service_pb2 as fw
import firewall_service_pb2_grpc

def add_firewall_filter(intf):
channel = grpc.insecure_channel('vqfx:32767')
auth_stub = authentication_service_pb2_grpc.LoginStub(channel)
response = auth_stub.LoginCheck(
auth.LoginRequest(
user_name='antidote',
password='antidotepassword',
client_id='jet',
)
)

fw_stub = firewall_service_pb2_grpc.AclServiceStub(channel)
filter = fw.AccessList(
acl_name='filter-by-jet',
acl_type=fw.ACL_TYPE_CLASSIC,
acl_family=fw.ACL_FAMILY_INET,
acl_flag=fw.ACL_FLAGS_NONE,
ace_list=[
fw.AclEntry(inet_entry=fw.AclInetEntry(
ace_name='t1',
ace_op=fw.ACL_ENTRY_OPERATION_ADD,
adjacency=fw.AclAdjacency(type=fw.ACL_ADJACENCY_AFTER),
matches=fw.AclEntryMatchInet(match_protocols=[fw.AclMatchProtocol(min=1, max=1, match_op=fw.ACL_MATCH_OP_EQUAL)]),
actions=fw.AclEntryInetAction(
action_t=fw.AclEntryInetTerminatingAction(action_accept=1),
actions_nt=fw.AclEntryInetNonTerminatingAction(action_log=1)
)
)),
fw.AclEntry(inet_entry=fw.AclInetEntry(
ace_name='t2',
ace_op=fw.ACL_ENTRY_OPERATION_ADD,
adjacency=fw.AclAdjacency(type=fw.ACL_ADJACENCY_AFTER),
actions=fw.AclEntryInetAction(
action_t=fw.AclEntryInetTerminatingAction(action_discard=1),
actions_nt=fw.AclEntryInetNonTerminatingAction(action_log=1)
),
))
]
)
fw_stub.AccessListAdd(filter)

fw_stub.AccessListBindAdd(
fw.AccessListObjBind(
acl=filter,
obj_type=fw.ACL_BIND_OBJ_TYPE_INTERFACE,
bind_object=fw.AccessListBindObjPoint(intf=intf),
bind_direction=fw.ACL_BIND_DIRECTION_INPUT,
bind_family=fw.ACL_FAMILY_INET
)
)
Binary file added lessons/lesson-25/jet-idl-17.4R1.16.tar.gz
Binary file not shown.
63 changes: 63 additions & 0 deletions lessons/lesson-25/stage1/configs/vqfx.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
version 17.4R1.16;
system {
host-name vqfx;
root-authentication {
encrypted-password "$6$yGARIqqs$.pxKXsYWySQy8BzpcuynxpBzXd7Z2tjqHlCR9wIS/MkEl1NrAsV/ZwNrJCtCrWQOovlmEgPvWXRzcPAykvCY8/"; ## SECRET-DATA
}
login {
user antidote {
uid 2000;
class super-user;
authentication {
encrypted-password "$6$L1CNVBaS$lxgFvGaqn5ixBoCNfNod44gQ26.3IYI.hurq/pL0a9Gqq8837HfP0iMZ7SKpzTF34wNmbK4axz4EIijAtLHRl0"; ## SECRET-DATA
}
}
password {
change-type set-transitions;
minimum-changes 0;
}
}
services {
ssh {
root-login allow;
}
netconf {
ssh;
rfc-compliant;
}
rest {
http {
port 8080;
}
enable-explorer;
}
}
syslog {
user * {
any emergency;
}
file messages {
any notice;
authorization info;
}
file interactive-commands {
interactive-commands any;
}
}
}
interfaces {
em0 {
unit 0 {
family inet {
address 10.0.0.15/24;
}
}
}
em1 {
unit 0 {
family inet {
address 169.254.0.2/24;
}
}
}
}
48 changes: 48 additions & 0 deletions lessons/lesson-25/stage1/guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
## Automating with JET
jnpr-raylam marked this conversation as resolved.
Show resolved Hide resolved

**Contributed by: [@valjeanchan](https://github.com/valjeanchan) and [@jnpr-raylam](https://github.com/jnpr-raylam)**

---

### Chapter 1 - JET rpc/notification configuration

#### What is JET?
Juniper Extension Toolkit (JET) is a framework that exposes API functionality made available by the internal Junos OS daemons. Each internal daemon exposes its own APIs. All of the APIs are accessible using the gRPC framework for remote procedure calls (RPCs).

JET supports the following:
* Multiple languages for applications that run off-box
* Python for applications that run on a device running Junos OS
* Applications written in C to run on devices that do not use the JET APIs
* An event notification method that enables the applications to respond to selected system events

There are two types of services JET provides:
* Request-response - An application can issue a request and wait for the response from Junos OS. (RPC model, gRPC based)
* Notification - An application can receive asynchronous notification of events happening on Junos OS. (publish-subscribe model. MQTT based)

For more informations about the JET can be found <a href="https://www.juniper.net/documentation/en_US/jet18.4/topics/concept/jet-architecture.html" target="_blank">here</a>.

In this lab we are going to explore a off-box python JET application.

#### Config the Junos device for JET
First of all, to run an off-box JET application, we need to enable the request-response configuration on the Junos OS device.

The gRPC can be run in clear-text mode _(insecure! that's why it is hidden and for lab test only!)_ or SSL encrypted mode for enhanced security. For simpilicity we'll go with clear-text in the lab. More information about gRPC over SSL can be found <a href="https://www.juniper.net/documentation/en_US/jet18.4/topics/topic-map/jet-off-box-apps.html" target="_blank">here</a>.
jnpr-raylam marked this conversation as resolved.
Show resolved Hide resolved

Apply below configuration to enable notification and gRPC request-response service on vQFX.

```
configure
set system services extension-service notification port 1883 allow-clients address 0.0.0.0/0
set system services extension-service request-response grpc clear-text port 32767
commit and-quit
```
<button type="button" class="btn btn-primary btn-sm" onclick="runSnippetInTab('vqfx', 0)">Run this snippet</button>

We can check the listening port to verify the notification and gRPC service are enabled.

```
show system connections | match LISTEN | match "\.1883|\.32767"
```
<button type="button" class="btn btn-primary btn-sm" onclick="runSnippetInTab('vqfx', 1)">Run this snippet</button>

Now the Junos OS device is ready for off-box JET applications and it's time to get some action! In the next chapter, we'll go through the notifiaction mechanism and collect some events from the MQTT event bus.
78 changes: 78 additions & 0 deletions lessons/lesson-25/stage2/configs/vqfx.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
version 17.4R1.16;
system {
host-name vqfx;
root-authentication {
encrypted-password "$6$yGARIqqs$.pxKXsYWySQy8BzpcuynxpBzXd7Z2tjqHlCR9wIS/MkEl1NrAsV/ZwNrJCtCrWQOovlmEgPvWXRzcPAykvCY8/"; ## SECRET-DATA
}
login {
user antidote {
uid 2000;
class super-user;
authentication {
encrypted-password "$6$L1CNVBaS$lxgFvGaqn5ixBoCNfNod44gQ26.3IYI.hurq/pL0a9Gqq8837HfP0iMZ7SKpzTF34wNmbK4axz4EIijAtLHRl0"; ## SECRET-DATA
}
}
password {
change-type set-transitions;
minimum-changes 0;
}
}
services {
ssh {
root-login allow;
}
extension-service {
request-response {
grpc {
clear-text {
port 32767;
}
}
}
notification {
port 1883;
allow-clients {
address 0.0.0.0/0;
}
}
}
netconf {
ssh;
rfc-compliant;
}
rest {
http {
port 8080;
}
enable-explorer;
}
}
syslog {
user * {
any emergency;
}
file messages {
any notice;
authorization info;
}
file interactive-commands {
interactive-commands any;
}
}
}
interfaces {
em0 {
unit 0 {
family inet {
address 10.0.0.15/24;
}
}
}
em1 {
unit 0 {
family inet {
address 169.254.0.2/24;
}
}
}
}
87 changes: 87 additions & 0 deletions lessons/lesson-25/stage2/guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
## Automating with JET
jnpr-raylam marked this conversation as resolved.
Show resolved Hide resolved

**Contributed by: [@valjeanchan](https://github.com/valjeanchan) and [@jnpr-raylam](https://github.com/jnpr-raylam)**

---

### Chapter 2 - Receive events from notification service using MQTT client
#### Message Broker and PUB-SUB model
Before we deep dive into notification service, let's get an introduction on message broker and publish-subscribe (PUB-SUB) model.

![JET System Archtecture](https://www.juniper.net/documentation/images/g043543.png)


A message broker system is hub and spoke based model where
- Broker accepts messages from clients and delivers to other interested clients
- Client either publish an message with a topic, or subscribe to a topic or both
- Topic is a namespace on the broker. Client subscribes or publish to a topics
- Publish: a Client sending a message to a Broker with a topic
- Subscribe: a Client requesting broker what topic it is interested.The broker will deliver messages to this client based on the topic it subscribed.

#### JET Notification Service
Juniper JET notification service is a message broker system based on <a href="http://mqtt.org/" target="_blank">MQTT</a> protocol to deliver system events. Junos system daemons such as RPD will generate messages and publish them to the JET message broker through eventd with specific topics. For example, interface events (link up/down) will have topic `/junos/events/kernel/interfaces` while route table related event will have topic `/junos/events/kernel/route-table`. The list of topic available can be found <a href="https://www.juniper.net/documentation/en_US/jet17.4/topics/concept/jet-notification-api-overview.html" target="_blank">here</a>.


#### Creating a Python MQTT Client
We're going to create a Python MQTT client to collect JET notification events.

First of all, go to the Python interactive prompt and import the MQTT module.

```
python
import paho.mqtt.client as mqtt
```
<button type="button" class="btn btn-primary btn-sm" onclick="runSnippetInTab('linux', 0)">Run this snippet</button>

After that, create a MQTT client object. Then define the `on_connect` callback function, which is triggered once the client connects to the JET MQTT broker, to subscribe the event. Here, we will subscribe to the topic `/junos/events/kernel/interfaces/ifa/add` which includes all new interfaces address events.
At last we bind the on_connect function to the client object.

```
client = mqtt.Client()

def on_connect(client, userdata, flags, rc):
client.subscribe("/junos/events/kernel/interfaces/ifa/add/#")

client.on_connect = on_connect
```
<button type="button" class="btn btn-primary btn-sm" onclick="runSnippetInTab('linux', 1)">Run this snippet</button>

Next, define `on_message` callback function, which is triggered whatever a message is received, to print the notification message payload. Then we bind the on_message function to the client object.

```
def on_message(client, userdata, msg):
print("%s %s" % (msg.topic, msg.payload))

client.on_message = on_message
```
<button type="button" class="btn btn-primary btn-sm" onclick="runSnippetInTab('linux', 2)">Run this snippet</button>

Finally, instruct the client to connect to vQFX TCP port 1883 we configured in previous stage, and start the main event loop function `loop_forever()` to wait for events.

```
client.connect('vqfx', 1883, 60)
client.loop_forever()
```
<button type="button" class="btn btn-primary btn-sm" onclick="runSnippetInTab('linux', 3)">Run this snippet</button>

The client is now ready to received JET notification event.


#### Triggering a event

Now, we try to trigger a new interface address event by create an new interface on vQFX.

```
configure
set interfaces xe-0/0/0 unit 0 family inet address 192.168.10.1/24
commit and-quit
```
<button type="button" class="btn btn-primary btn-sm" onclick="runSnippetInTab('vqfx', 4)">Run this snippet</button>

Once the commit is completed, Eventd will receive the new IFA event and deliver it to all clients who subscribed the IFA topic.

Now change to `linux` terminal, you should be able to see the `KERNEL_EVENT_IFA_ADD` event.

Press `Ctrl-C` to exit the loop.

In the next chapter we are going to explore JET request-response calls.
Loading