Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
9c3c07a
Print del_path
M-Vivek-Juniper May 23, 2024
cddec2a
Merge pull request #36 from Juniper/delpath
amohit1315 May 23, 2024
7e291a8
Merge pull request #37 from Juniper/flat-formatting
M-Vivek-Juniper May 31, 2024
46fa0e4
fixed panic error when parseOutput.jHeader is nill
amohit1315 Jun 11, 2024
5e8fd2d
Merge pull request #40 from Juniper/ccl_issue
amohit1315 Jun 11, 2024
a8c2d5d
added override control of eos in internal jtimon configs
amohit1315 Jun 20, 2024
d17b94b
added auto unit tests update
amohit1315 Jun 21, 2024
dd154db
fixed eos enhancement and subscription issue for single path
amohit1315 Jun 25, 2024
c7a5d46
Merge pull request #42 from Juniper/eos_internal
nsimariaj Jun 25, 2024
5c00b24
output stat summary to terminal if internal jtimon csv stats is enabled
amohit1315 Jul 3, 2024
b3c832b
Merge pull request #43 from Juniper/output_stat_summary
amohit1315 Jul 8, 2024
4fa5ee0
added support for leaf-list for gnmi and pre-gnmi
amohit1315 Sep 21, 2024
1cd2978
updated parserd string
amohit1315 Sep 21, 2024
3d203a0
Merge pull request #49 from Juniper/influx_leaf_list_support
nileshsimaria Sep 21, 2024
0e1bc07
enable csv logging without internal-jtimon logging
Oct 17, 2024
f1c4bc1
updated new tests
Oct 17, 2024
dfc0441
added hex and float fix
Oct 17, 2024
a566e99
Merge pull request #52 from Juniper/fix_csv_input
nsimariaj Oct 17, 2024
0464cab
updated conflict
Oct 17, 2024
a29d4b0
when json output is configured, print in json format
amohit1315 Oct 18, 2024
d6fccff
Merge pull request #57 from Juniper/json_out_gnmi
amohit1315 Oct 18, 2024
4a3c218
remove extra escape if present and fixed regression for internal jtim…
amohit1315 Oct 18, 2024
98b4e55
Merge pull request #58 from Juniper/fix_pre_gnmi_regression
amohit1315 Oct 18, 2024
c2c3656
added json out flag check and printing json output for pre-gnmi for i…
amohit1315 Oct 18, 2024
6a3fdfe
Merge pull request #59 from Juniper/json_out_pre_gnmi
amohit1315 Oct 18, 2024
75d77de
Merge pull request #53 from Juniper/byte_float_hex_fix
amohit1315 Oct 29, 2024
9d216ed
fixed incomplete outputs
amohit1315 Feb 26, 2025
5014165
Merge pull request #64 from Juniper/fix_output_formatting
nileshsimaria Feb 27, 2025
f7c600c
add sample config for gnmi target-defined
nsimariaj Jun 12, 2025
f1c02d7
Support IPv6
nsimariaj Jun 24, 2025
3cef8bb
added support for target and heartbeat interval
amohit1315 Oct 13, 2025
9e03b91
Merge pull request #67 from Juniper/target_heartbeat_support
nileshsimaria Oct 13, 2025
fded167
fix heartbeat interval
amohit1315 Oct 22, 2025
23982f7
Merge pull request #69 from Juniper/heartbeat_fix
amohit1315 Oct 22, 2025
60840f5
support once subscription
amohit1315 Oct 24, 2025
31eb758
Merge remote-tracking branch 'origin/master' into once_subscription
amohit1315 Oct 24, 2025
339e97d
Merge pull request #70 from Juniper/once_subscription
amohit1315 Oct 24, 2025
e9608cb
added formatting output
amohit1315 Jan 14, 2026
203c84b
testing
amohit1315 Jan 14, 2026
04b0711
testing new
amohit1315 Jan 14, 2026
648d78f
Added formatting and once subscription support
amohit1315 Jan 14, 2026
c5ff0bc
Merge pull request #72 from Juniper/once_subscription
amohit1315 Jan 14, 2026
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
57 changes: 57 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,63 @@ eos : end of sync. Tell Junos to send end of sync for on-change subscriptions.
grpc/ws : window size of grpc for slower clients
</pre>


## IPv6 Support

jtimon supports both IPv4 and IPv6 addresses for connecting to network devices.

### IPv4 Configuration
```json
{
"host": "192.168.1.1",
"port": 32767,
"user": "username",
"password": "password",
"paths": [
{
"path": "/interfaces",
"freq": 10000
}
]
}
```

### IPv6 Configuration
When using IPv6 addresses, wrap the address in square brackets `[]`:

```json
{
"host": "[2001:db8::1]",
"port": 32767,
"user": "username",
"password": "password",
"paths": [
{
"path": "/interfaces",
"freq": 10000
}
]
}
```

**Note**: The square brackets are required for IPv6 addresses to properly distinguish the address from the port number, following RFC 3986 standards.

### Examples of Valid IPv6 Formats
- `"[::1]"` - IPv6 loopback
- `"[2001:db8::1]"` - Full IPv6 address
- `"[fe80::1%eth0]"` - Link-local with zone identifier
- `"[::ffff:192.0.2.1]"` - IPv4-mapped IPv6 address

### Command Line Usage
When using command line options for dial-out or server mode:
```bash
# IPv4
./jtimon --host 127.0.0.1 --port 32767

# IPv6
./jtimon --host ::1 --port 32767
```

## Kafka Publish

To publish gRPC/Openconfig JTI data to Kafka, use the following json config.
Expand Down
17 changes: 11 additions & 6 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ type Config struct {
// GnmiConfig definition
type GnmiConfig struct {
Encoding string
Mode int16
}

// VendorConfig definition
Expand Down Expand Up @@ -95,12 +96,14 @@ type TLSConfig struct {

// PathsConfig to specify subscription path, reporting-interval (freq), etc,.
type PathsConfig struct {
Path string `json:"path"`
Freq uint64 `json:"freq"`
Mode string `json:"mode"`
Origin string `json:"origin"`
PreGnmi bool `json:"pre-gnmi"`
Gnmi bool `json:"gnmi"`
Path string `json:"path"`
Freq uint64 `json:"freq"`
Mode string `json:"mode"`
Origin string `json:"origin"`
Target string `json:"target"`
PreGnmi bool `json:"pre-gnmi"`
Gnmi bool `json:"gnmi"`
Gnmi_heartbeat_interval uint64 `json:"gnmi_heartbeat_interval"`
}

// NewJTIMONConfigFilelist to return configfilelist object
Expand Down Expand Up @@ -323,6 +326,7 @@ func HandleConfigChange(jctx *JCtx, config Config, restart *bool) error {
jctx.config = config
logInit(jctx)
internalJtimonLogInit(jctx)
initInternalJtimon(jctx)
if restart != nil {
jLog(jctx, fmt.Sprintf("Restarting worker process to spawn new device connection for: %s", jctx.file))
*restart = true
Expand All @@ -348,6 +352,7 @@ func ConfigRead(jctx *JCtx, init bool, restart *bool) error {
jctx.config = config
logInit(jctx)
internalJtimonLogInit(jctx)
initInternalJtimon(jctx)
b, err := json.MarshalIndent(jctx.config, "", " ")
if err != nil {
return fmt.Errorf("config parsing error (json marshal) for %s: %v", jctx.file, err)
Expand Down
69 changes: 42 additions & 27 deletions gnmi_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package main

import (
"bytes"
"encoding/binary"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
Expand Down Expand Up @@ -72,48 +74,47 @@ type gnmiParseOutputT struct {
inKvs uint64
}


// parseKeyValuePairs takes an input string in the format `prefix[key = "value"]`
// and returns the prefix as a string and the key-value pairs as a map.
//
// The input string can contain multiple key-value pairs in the same or different brackets.
// The keys and values are trimmed of quotes and spaces.
//
// If the input string does not contain any brackets, the function returns the entire string
// If the input string does not contain any brackets, the function returns the entire string
// as the prefix and an empty map.
func parseKeyValuePairs(input string) (string, map[string]string) {
// Initialize a map to hold the key-value pairs
result := make(map[string]string)
// Initialize a map to hold the key-value pairs
result := make(map[string]string)

// Split the input string by the first occurrence of [" to get the prefix and the rest
parts := strings.SplitN(input, "[", 2)
prefix := parts[0]
// Split the input string by the first occurrence of [" to get the prefix and the rest
parts := strings.SplitN(input, "[", 2)
prefix := parts[0]

// Match anything inside square brackets
re := regexp.MustCompile(`\[(.*?)\]`)
matches := re.FindAllString(input, -1)
// Match anything inside square brackets
re := regexp.MustCompile(`\[(.*?)\]`)
matches := re.FindAllString(input, -1)

for _, match := range matches {
// Remove the square brackets
match = strings.Trim(match, "[]")
for _, match := range matches {
// Remove the square brackets
match = strings.Trim(match, "[]")

// Split by "and" to support multiple key-value pairs in the same brackets
pairs := strings.Split(match, "and")
// Split by "and" to support multiple key-value pairs in the same brackets
pairs := strings.Split(match, "and")

for _, pair := range pairs {
// Split by "=" to separate the key and value
kv := strings.Split(pair, "=")
for _, pair := range pairs {
// Split by "=" to separate the key and value
kv := strings.Split(pair, "=")

// Trim the quotes and spaces from the key and value
key := strings.Trim(kv[0], " \"")
value := strings.Trim(kv[1], " \"")
// Trim the quotes and spaces from the key and value
key := strings.Trim(kv[0], " \"")
value := strings.Trim(kv[1], " \"")

// Add the key-value pair to the result map
result[key] = value
}
}
// Add the key-value pair to the result map
result[key] = value
}
}

return prefix, result
return prefix, result
}

// Convert xpath to gNMI path
Expand Down Expand Up @@ -359,7 +360,16 @@ func gnmiParseValue(gnmiValue *gnmi.TypedValue, ts bool, enableUint bool) (inter
case *gnmi.TypedValue_BoolVal:
value = gnmiValue.GetBoolVal()
case *gnmi.TypedValue_BytesVal:
value = gnmiValue.GetBytesVal()
byteVal := gnmiValue.GetBytesVal()
if len(byteVal) == 4 {
var double_val float32
if err := binary.Read(bytes.NewReader(byteVal), binary.LittleEndian, &double_val); err != nil {
value = hex.EncodeToString(byteVal)
}
value = fmt.Sprintf("%0.2f", double_val)
} else {
value = hex.EncodeToString(byteVal)
}
case *gnmi.TypedValue_AsciiVal:
value = gnmiValue.GetAsciiVal()
case *gnmi.TypedValue_AnyVal:
Expand Down Expand Up @@ -529,3 +539,8 @@ func formJuniperTelemetryHdr(jXpaths *jnprXpathDetails, gnmiExt []*gnmi_ext1.Ext
return &juniperHdrDetails, true, nil

}

func gnmiHeartBeat(hb uint64) uint64 {
hb_val := (hb * gGnmiFreqUnits) / 10000
return hb_val
}
2 changes: 1 addition & 1 deletion grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func getGPRCDialOptions(jctx *JCtx, vendor *vendor) ([]grpc.DialOption, error) {
return nil, err
}

if *stateHandler {
if *statsHandler {
opts = append(opts, grpc.WithStatsHandler(&statshandler{jctx: jctx}))
if isCsvStatsEnabled(jctx) {
jctx.config.InternalJtimon.csvLogger.Printf(fmt.Sprintf("%s,%s,%s,%s,%s,%s,%s,%s,%s\n",
Expand Down
11 changes: 11 additions & 0 deletions influx.go
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,17 @@ func addIDB(ocData *na_pb.OpenConfigData, jctx *JCtx, rtime time.Time) {
value32 := v.GetFloatValue()
checkAndCeilFloatValues(&value32, nil, &floatVal)
kv[xmlpath] = floatVal
case *na_pb.KeyValue_LeaflistValue:
e := v.GetLeaflistValue().Element
var leafListStr string
for _, elem := range e {
switch elem.Value.(type) {
case *na_pb.TypedValue_LeaflistStrValue:
llStrValue := elem.GetLeaflistStrValue()
leafListStr = leafListStr + llStrValue + ","
}
}
kv[xmlpath] = leafListStr[:len(leafListStr)-1]
default:
}

Expand Down
Loading