Skip to content

Commit

Permalink
feat: Implemented mib parser
Browse files Browse the repository at this point in the history
  • Loading branch information
umutteroll07 committed Oct 30, 2024
1 parent f9b8a80 commit 7665802
Show file tree
Hide file tree
Showing 5 changed files with 356 additions and 0 deletions.
52 changes: 52 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# mibparser

This project is used to convert mib file to json format. <br/>
The created json format is in tree format. <br/>
This code takes the nodes id and the nodes parent id and finally creates an OID with this information <br/>
Informs the user if another mib file is needed for the created json format <br/>

### Requirements
There must be required mib files in the specified path <br/>

### Installation

```
go get github.com/KadirOzerOzturk/mibparser
```

### Usage

```go
package main

import (
"fmt"
"log"

"github.com/KadirOzerOzturk/mibparser"
)

func main() {
mibparserObject, err := mibparser.Load(mibparser.NewPath("./mib-files"))
if err != nil {
log.Fatalf("error when loading mibparser path")
}
mibTree, err := mibparserObject.GetJSONTree()
if err != nil {
log.Fatalf("error when parsing mib files, err " + err.Error())
}
fmt.Println("mibTree ", mibTree)

mibObjects, err := mibparserObject.GetObjects()
if err != nil {
log.Fatalf("error when parsing mib files, err " + err.Error())
}
fmt.Println("mibObjects", mibObjects)
}


```
# Created by

<img src="https://avatars.githubusercontent.com/u/63673212?s=280&v=4" alt="Logo" width="150" height="150">

3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/KadirOzerOzturk/mibparser

go 1.22.2
49 changes: 49 additions & 0 deletions load.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package mibparser

import (
"bufio"
"fmt"
"log"
"os"
)

// Load initializes the MIBParser with options
func Load(opts ...Option) (*MIBParser, error) {
opt := Opts{}
for _, o := range opts {
o(&opt)
}
return &MIBParser{opts: opt}, nil
}

// ReadMIBFile reads all MIB files in the directory
func (p *MIBParser) ReadMIBFile() ([]string, error) {
files, err := os.ReadDir(p.opts.Path)
if err != nil {
log.Fatal(err)
}
var mergedMIB []string
for _, file := range files {
lines, err := readMIBFileWithPath(p.opts.Path + "/" + file.Name())
if err != nil {
fmt.Println("Error reading MIB file:", err) // Print error if a file can't be read
}
mergedMIB = append(mergedMIB, lines...) // Merge lines into a single slice
}
return mergedMIB, nil
}

// readMIBFileWithPath reads a single MIB file and returns its lines
func readMIBFileWithPath(filePath string) ([]string, error) {
file, err := os.Open(filePath)
if err != nil {
return nil, err
}
defer file.Close() // Ensure the file is closed when function exits
var lines []string
scanner := bufio.NewScanner(file)
for scanner.Scan() {
lines = append(lines, scanner.Text()) // Read each line into the slice
}
return lines, scanner.Err()
}
21 changes: 21 additions & 0 deletions mibparser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package mibparser

// Option is a configuration function type
type Option func(*Opts)

// Opts holds configuration options
type Opts struct {
Path string
}

// MIBParser stores configuration options
type MIBParser struct {
opts Opts
}

// NewPath sets the path
func NewPath(path string) Option {
return func(opts *Opts) {
opts.Path = path
}
}
231 changes: 231 additions & 0 deletions parse.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
package mibparser

import (
"encoding/json"
"fmt"
"strings"
)

// OIDNode represents a node in the MIB tree
type OIDNode struct {
Name string `json:"name"`
OID string `json:"oid"`
ID string `json:"id"`
Parent string `json:"parent"`
Description string `json:"description"`
Children []*OIDNode `json:"children,omitempty"`
}

// Parse reads and parses MIB files into OIDNodes
func (p *MIBParser) Parse() ([]OIDNode, error) {
lines, err := p.ReadMIBFile()
if err != nil {
return nil, err
}
nodes, err := parseMIB(lines)
if err != nil {
return nil, err
}
return nodes, err
}

// GetJSONTree returns a JSON representation of the MIB tree
func (p *MIBParser) GetJSONTree() (string, error) {
nodes, err := p.Parse()
if err != nil {
return "", err
}
tree, err := buildTree(nodes)
if err != nil {
return "", err
}
byteNodes, err := json.Marshal(tree)

return string(byteNodes), nil
}

// GetObjects returns a JSON string of the parsed OIDNodes
func (p *MIBParser) GetObjects() (string, error) {
nodes, err := p.Parse()
if err != nil {
return "", err
}

byteNodes, err := json.Marshal(nodes) // Marshal nodes to JSON
if err != nil {
return "", err
}
return string(byteNodes), nil
}

// buildTree constructs the MIB tree from OIDNodes
func buildTree(nodes []OIDNode) ([]*OIDNode, error) {
nodeMap := make(map[string]*OIDNode)
// Initialize the nodeMap
for i := range nodes {
node := &nodes[i]
nodeMap[node.Name] = node
}

var rootNodes []*OIDNode
rootNames := map[string]bool{"iso": true}
for i := range nodes {
node := &nodes[i]
if rootNames[node.Parent] {
rootNodes = append(rootNodes, node)
} else if parent, exists := nodeMap[node.Parent]; exists {
parent.Children = append(parent.Children, node)
}
}
return rootNodes, nil
}

// parseMIB parses MIB lines into OIDNodes
func parseMIB(lines []string) ([]OIDNode, error) {
var requiredMibs []string
var definingMibs []string
// Parse required and defining MIBs
for i := 0; i < len(lines); i++ {
line := strings.TrimSpace(lines[i])
if strings.Contains(line, "FROM") {
parts := strings.Split(line, "FROM")
if len(parts) >= 2 {
subParts := strings.Split(parts[1], ";")
if len(subParts) > 0 {
requiredMib := strings.TrimSpace(subParts[0])
requiredMibs = append(requiredMibs, requiredMib+".mib")
}
}
}
if strings.Contains(line, "DEFINITIONS ::= BEGIN") {
parts := strings.Split(line, "DEFINITIONS ::= BEGIN")
if len(parts) > 0 {
definingMib := strings.TrimSpace(parts[0])
definingMibs = append(definingMibs, definingMib+".mib")
}
}
}
// Remove redundant required MIBs
for i := len(requiredMibs) - 1; i >= 0; i-- {
for _, definingMib := range definingMibs {
if definingMib == requiredMibs[i] {
requiredMibs = append(requiredMibs[:i], requiredMibs[i+1:]...)
break
}
}
}
uniqueMap := make(map[string]bool)
uniqueReqireds := []string{}
for _, item := range requiredMibs {
if _, found := uniqueMap[item]; !found {
uniqueMap[item] = true
uniqueReqireds = append(uniqueReqireds, item)
}
}
if len(uniqueReqireds) > 0 {
return nil, fmt.Errorf("parsing operation has failed :\n \t\t\t\t\trequired files: %s", uniqueReqireds)
}
var nodes []OIDNode
// Parse lines into OIDNodes
for i := 0; i < len(lines); i++ {
line := strings.TrimSpace(lines[i])
if strings.Contains(line, "OBJECT IDENTIFIER ::= {") {
parts := strings.Split(line, "OBJECT IDENTIFIER ::= {")
if len(parts) >= 2 {
nameParts := strings.Split(parts[0], " ")
nextNodeParts := strings.Split(strings.TrimSpace(strings.Trim(parts[1], "}")), " ")
if len(nameParts) > 0 && len(nextNodeParts) >= 2 {
name := strings.TrimSpace(nameParts[0])
nodes = append(nodes, OIDNode{
Name: name,
ID: nextNodeParts[1],
Parent: nextNodeParts[0],
Description: "",
})
}
}
} else if (strings.Contains(line, "OBJECT-TYPE") || strings.Contains(line, "OBJECT-IDENTITY")) && !strings.Contains(line, "MODULE-IDENTITY") {
nameParts := strings.Fields(line)
if len(nameParts) > 0 {
name := strings.TrimSpace(nameParts[0])
parent := ""
description := ""
for j := i + 1; j < len(lines); j++ {
nextLine := strings.TrimSpace(lines[j])
if strings.HasPrefix(nextLine, "::= {") {
parentParts := strings.Split(nextLine, "{")
if len(parentParts) > 1 {
parent = strings.Trim(parentParts[1], " }")
}
break
}
if strings.HasPrefix(nextLine, "DESCRIPTION") {
descriptionLine := nextLine
for k := j + 1; k < len(lines); k++ {
if strings.Contains(lines[k], "::= {") {
break
}
descriptionLine += " " + strings.TrimSpace(lines[k])
}
description = strings.TrimSpace(strings.ReplaceAll(description, "DESCRIPTION", ""))
}
}
if !strings.Contains(name, "OBJECT-TYPE") && !strings.Contains(name, "--") {
parentParts := strings.Split(parent, " ")
if len(parentParts) >= 2 {
nodes = append(nodes, OIDNode{
Name: name,
ID: parentParts[1],
Parent: parentParts[0],
Description: description,
})
}
}
}
}
}
formatedNodes, err := setOids(nodes) // Set OIDs for nodes
if err != nil {
return nil, err
}
return formatedNodes, nil
}

func setOids(nodes []OIDNode) ([]OIDNode, error) {

// Initialize formatted nodes slice
var formatedNodes []OIDNode

// Create a map to store nodes by name
nodeMap := make(map[string]OIDNode)
for _, node := range nodes {
nodeMap[node.Name] = node
}

// Iterate over nodes to set OIDs
for _, node := range nodes {
oidParts := []string{node.ID} // Start with current node ID
parent := node.Parent

for parent != "" {
if nextNode, found := nodeMap[parent]; found {
oidParts = append([]string{nextNode.ID}, oidParts...)
parent = nextNode.Parent

} else {
break
}
}

oid := strings.Join(oidParts, ".") // Join all parts to form full OID
formatedNodes = append(formatedNodes, OIDNode{
Name: node.Name,
ID: node.ID,
Parent: node.Parent,
OID: "1." + oid,
Description: node.Description,
})
}

return formatedNodes, nil
}

0 comments on commit 7665802

Please sign in to comment.