From 3cad4a0897bbdfbbe9422cc3a077c22c37b37522 Mon Sep 17 00:00:00 2001 From: Yunkon Kim Date: Wed, 10 Jan 2024 19:49:22 +0900 Subject: [PATCH] Update `netutil` and example * Update models * Add GetPrefix(), GetNetmask() * Add Cobra for enhanced CLI argument parsing and help functionality --- .gitignore | 1 + go.mod | 2 + go.sum | 6 + src/core/common/netutil/netutil.go | 94 ++++++++++---- src/examples/netutil/README.md | 40 +++++- src/examples/netutil/netutil.go | 189 +++++++++++++++++++++-------- 6 files changed, 257 insertions(+), 75 deletions(-) diff --git a/.gitignore b/.gitignore index 28ec051fd..487ae81a4 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ src/cb-tumblebug src/cb-tumblebug-arm src/cli/tbctl src/cli/cli +src/examples/netutil/netutil # AutoGenerated DB data directory meta_db/ diff --git a/go.mod b/go.mod index 741139b85..bd802e8eb 100644 --- a/go.mod +++ b/go.mod @@ -44,6 +44,7 @@ require ( github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/hashicorp/hcl v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/labstack/gommon v0.4.0 // indirect @@ -61,6 +62,7 @@ require ( github.com/snowzach/rotatefilehook v0.0.0-20220211133110-53752135082d // indirect github.com/spf13/afero v1.9.5 // indirect github.com/spf13/cast v1.5.1 // indirect + github.com/spf13/cobra v1.8.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.4.2 // indirect diff --git a/go.sum b/go.sum index f4ef933c4..9e9db6793 100644 --- a/go.sum +++ b/go.sum @@ -78,6 +78,7 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7 github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -218,6 +219,8 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= @@ -346,6 +349,7 @@ github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= @@ -360,6 +364,8 @@ github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= diff --git a/src/core/common/netutil/netutil.go b/src/core/common/netutil/netutil.go index db30222a8..c35121cd8 100644 --- a/src/core/common/netutil/netutil.go +++ b/src/core/common/netutil/netutil.go @@ -39,26 +39,66 @@ func init() { } // Models +type NetworkConfig struct { + BaseNetwork Network `json:"baseNetwork"` +} + +// NetworkInterface defines the methods that both Network and NetworkDetails should implement. +type NetworkInterface interface { + GetCIDRBlock() string + GetSubnets() []Network +} + type Network struct { - CIDRBlock string // 192.168.0.0/24 - NetworkAddress string // 192.168.0.0 - BroadcastAddress string // 192.168.0.255 - Prefix int // 24 - Netmask string // 255.255.255.0 - HostCapacity int // 254 - Subnets []Network // Subnets within this network + CIDRBlock string `json:"cidrBlock"` + Name string `json:"name,omitempty"` + Subnets []Network `json:"subnets,omitempty"` } -// New creates a new Network object. -func New(cidrBlock string) (*Network, error) { - network := &Network{ - CIDRBlock: cidrBlock, +func (n *Network) GetName() string { return n.Name } +func (n *Network) GetCIDRBlock() string { return n.CIDRBlock } +func (n *Network) GetSubnets() []Network { return n.Subnets } + +// New creates a new NetworkDetails object. +func NewNetwork(cidrBlock string) (*Network, error) { + + _, _, err := net.ParseCIDR(cidrBlock) + if err != nil { + return nil, err } + network := new(Network) + network.CIDRBlock = cidrBlock + + network.Subnets = []Network{} + + return network, nil +} + +type NetworkDetails struct { + Network + NetworkAddress string `json:"networkAddress,omitempty"` + BroadcastAddress string `json:"broadcastAddress,omitempty"` + Prefix int `json:"prefix,omitempty"` + Netmask string `json:"netmask,omitempty"` + HostCapacity int `json:"hostCapacity,omitempty"` +} + +// Getters +func (n *NetworkDetails) GetNetworkAddress() string { return n.NetworkAddress } +func (n *NetworkDetails) GetBroadcastAddress() string { return n.BroadcastAddress } +func (n *NetworkDetails) GetPrefix() int { return n.Prefix } +func (n *NetworkDetails) GetNetmask() string { return n.Netmask } +func (n *NetworkDetails) GetHostCapacity() int { return n.HostCapacity } + +// New creates a new NetworkDetails object. +func NewNetworkDetails(cidrBlock string) (*NetworkDetails, error) { _, ipNet, err := net.ParseCIDR(cidrBlock) if err != nil { return nil, err } + network := new(NetworkDetails) + network.CIDRBlock = cidrBlock // Set Netmask mask := ipNet.Mask @@ -94,15 +134,6 @@ func New(cidrBlock string) (*Network, error) { return network, nil } -// Getters -func (n *Network) GetCIDRBlock() string { return n.CIDRBlock } -func (n *Network) GetNetworkAddress() string { return n.NetworkAddress } -func (n *Network) GetBroadcastAddress() string { return n.BroadcastAddress } -func (n *Network) GetPrefix() int { return n.Prefix } -func (n *Network) GetNetmask() string { return n.Netmask } -func (n *Network) GetHostCapacity() int { return n.HostCapacity } -func (n *Network) GetSubnets() []Network { return n.Subnets } - // SubnettingByMininumSubnetCount divides the CIDR block into subnets to accommodate the minimum number of subnets entered. func SubnettingByMininumSubnetCount(cidrBlock string, minSubnets int) ([]string, error) { _, network, err := net.ParseCIDR(cidrBlock) @@ -223,6 +254,27 @@ func GetBroadcastAddr(cidrBlock string) (string, error) { return CalculateBroadcastAddr(ipNet) } +// GetPrefix calculates the prefix for a given CIDR block. +func GetPrefix(cidrBlock string) (int, error) { + _, ipNet, err := net.ParseCIDR(cidrBlock) + if err != nil { + return -1, err + } + + prefix, _ := ipNet.Mask.Size() + return prefix, nil +} + +// GetNetmask calculates the netmask for a given CIDR block. +func GetNetmask(cidrBlock string) (string, error) { + _, ipNet, err := net.ParseCIDR(cidrBlock) + if err != nil { + return "", err + } + mask := ipNet.Mask + return net.IP(mask).String(), nil +} + // CalculateHostCapacity calculates the number of hosts that can be accommodated in a given IPNet. func CalculateHostCapacity(ipNet *net.IPNet) (int, error) { @@ -245,7 +297,7 @@ func CalculateHostCapacity(ipNet *net.IPNet) (int, error) { func GetSizeOfHosts(cidrBlock string) (int, error) { _, ipNet, err := net.ParseCIDR(cidrBlock) if err != nil { - return 0, err + return -1, err } return CalculateHostCapacity(ipNet) diff --git a/src/examples/netutil/README.md b/src/examples/netutil/README.md index 5494f46bc..489ed19e3 100644 --- a/src/examples/netutil/README.md +++ b/src/examples/netutil/README.md @@ -3,7 +3,7 @@ Thia is an example of `netutil` package. (see [netutil package](https://github.com/cloud-barista/cb-tumblebug/blob/main/src/core/common/netutil/netutil.go)) -### How to execute this example +### How to use this example **Move to the example directory** @@ -11,8 +11,42 @@ Thia is an example of `netutil` package. cd PROJECT_ROOT_DIR/src/examples/netutil ``` -**Execute the example** +**Build this example** ```bash -go run . +go build . +``` + +**See help** +```bash +./netutil --help +``` + +``` +This program demonstrates the usage of the netutil package. + +Usage: + ./netutil [flags] + +Examples: +./netutil --cidr "10.0.0.0/16" --minsubnets 4 --hosts 500 +or +./netutil -c "10.0.0.0/16" -s 4 -n 500 + +Flags: + -c, --cidr string Base network CIDR block (default "192.168.0.0/16") + -h, --help help for ./netutil + -n, --hosts int Number of hosts per subnet (default 500) + -s, --minsubnets int Minimum number of subnets required (default 4) +``` + +**Run example** +```bash +./netutil +``` +```bash +./netutil -c "10.0.0.0/16" -s 4 -n 500 +``` +```bash +./netutil --cidr "10.0.0.0/16" --minsubnets 4 --hosts 500 ``` diff --git a/src/examples/netutil/netutil.go b/src/examples/netutil/netutil.go index 786affabb..6b1ba980a 100644 --- a/src/examples/netutil/netutil.go +++ b/src/examples/netutil/netutil.go @@ -1,59 +1,48 @@ package main import ( + "encoding/json" "fmt" + "log" "github.com/cloud-barista/cb-tumblebug/src/core/common/netutil" + "github.com/spf13/cobra" ) func main() { - fmt.Println("netuil example") + var exampleCmd = &cobra.Command{ + Use: "./netutil", + Short: "Example program for the netutil package", + Long: ` +This program demonstrates the usage of the netutil package.`, + Example: `./netutil --cidr "10.0.0.0/16" --minsubnets 4 --hosts 500 +or +./netutil -c "10.0.0.0/16" -s 4 -n 500`, + Run: runExample, + } - cidrBlock := "192.168.0.0/16" + // Command-line flags with shorthand + exampleCmd.PersistentFlags().StringP("cidr", "c", "192.168.0.0/16", "Base network CIDR block") + exampleCmd.PersistentFlags().IntP("minsubnets", "s", 4, "Minimum number of subnets required") + exampleCmd.PersistentFlags().IntP("hosts", "n", 500, "Number of hosts per subnet") - fmt.Println("\nNetwork template example") - // Define the base network - baseNetwork := netutil.Network{ - CIDRBlock: "10.0.0.0/16", - Subnets: []netutil.Network{ - { - // Define a VPC Network - CIDRBlock: "10.0.1.0/24", - Subnets: []netutil.Network{ - { - // Define a Subnetwork within the VPC - CIDRBlock: "10.0.1.0/28", - }, - { - // Another Subnetwork within the VPC - CIDRBlock: "10.0.1.16/28", - }, - }, - }, - { - // Another VPC Network - CIDRBlock: "10.0.2.0/24", - Subnets: []netutil.Network{ - { - // Subnetwork within the second VPC - CIDRBlock: "10.0.2.0/28", - }, - }, - }, - }, + if err := exampleCmd.Execute(); err != nil { + log.Fatalf("Error executing netutil-example: %s", err) } +} - fmt.Println("Base Network CIDR:", baseNetwork.CIDRBlock) - for i, vpc := range baseNetwork.Subnets { - fmt.Printf("VPC Network %d CIDR: %s\n", i+1, vpc.CIDRBlock) - for j, subnet := range vpc.Subnets { - fmt.Printf("\tSubnetwork %d CIDR: %s\n", j+1, subnet.CIDRBlock) - } - } +func runExample(cmd *cobra.Command, args []string) { + + // Retrieve the flag values + cidrBlock, _ := cmd.Flags().GetString("cidr") + minSubnets, _ := cmd.Flags().GetInt("minsubnets") + hostsPerSubnet, _ := cmd.Flags().GetInt("hosts") + fmt.Println("Starting netuil example") + + /////////////////////////////////////////////////////////////////////////////////////////////////// fmt.Println("\nDivide CIDR block into subnets to accommodate at least minimum number of subnets") - minSubnets := 4 // Minimum number of subnets required fmt.Printf("Minimum number of subnets: %d\n", minSubnets) subnets, err := netutil.SubnettingByMininumSubnetCount(cidrBlock, minSubnets) @@ -64,9 +53,9 @@ func main() { for _, subnet := range subnets { fmt.Println(subnet) } - fmt.Println("\nDivide CIDR block by a specified number of hosts") - hostsPerSubnet := 500 // number of hosts you want in each subnet + /////////////////////////////////////////////////////////////////////////////////////////////////// + fmt.Println("\nDivide CIDR block by a specified number of hosts") fmt.Printf("Number of hosts per subnet: %d\n", hostsPerSubnet) subnets, err = netutil.SubnettingByHosts(cidrBlock, hostsPerSubnet) @@ -78,6 +67,7 @@ func main() { fmt.Println(subnet) } + /////////////////////////////////////////////////////////////////////////////////////////////////// fmt.Println("\nGet Network Address") networkAddress, err := netutil.GetNetworkAddr(cidrBlock) if err != nil { @@ -85,6 +75,7 @@ func main() { } fmt.Printf("Network Address: %s\n", networkAddress) + /////////////////////////////////////////////////////////////////////////////////////////////////// fmt.Println("\nGet Broadcast Address") broadcastAddress, err := netutil.GetBroadcastAddr(cidrBlock) if err != nil { @@ -93,6 +84,23 @@ func main() { } fmt.Printf("Broadcast Address: %s\n", broadcastAddress) + /////////////////////////////////////////////////////////////////////////////////////////////////// + fmt.Println("\nGet Prefix") + prefix, err := netutil.GetPrefix(cidrBlock) + if err != nil { + fmt.Println(err) + } + fmt.Printf("Prefix: %d\n", prefix) + + /////////////////////////////////////////////////////////////////////////////////////////////////// + fmt.Println("\nGet Netmask") + netmask, err := netutil.GetNetmask(cidrBlock) + if err != nil { + fmt.Println(err) + } + fmt.Printf("Netmask: %s\n", netmask) + + /////////////////////////////////////////////////////////////////////////////////////////////////// fmt.Println("\nCalculate the number of hosts that can be accomodated in a given CIDR block") hosts, err := netutil.GetSizeOfHosts(cidrBlock) @@ -102,15 +110,94 @@ func main() { fmt.Printf("The CIDR block %s can accommodate %d hosts.\n", cidrBlock, hosts) + /////////////////////////////////////////////////////////////////////////////////////////////////// fmt.Println("\nNew network") - baseNet, err := netutil.New(cidrBlock) - fmt.Printf("Base Network: %+v\n", baseNet) - fmt.Printf("GetCIDRBlock(): %s\n", baseNet.GetCIDRBlock()) - fmt.Printf("GetNetworkAddress(): %s\n", baseNet.GetNetworkAddress()) - fmt.Printf("GetBroadcastAddress(): %s\n", baseNet.GetBroadcastAddress()) - fmt.Printf("GetPrefix(): %d\n", baseNet.GetPrefix()) - fmt.Printf("GetNetmask(): %s\n", baseNet.GetNetmask()) - fmt.Printf("GetHostCapacity(): %d\n", baseNet.GetHostCapacity()) - fmt.Printf("GetSubnets(): %v\n", baseNet.GetSubnets()) + net, err := netutil.NewNetwork(cidrBlock) + fmt.Printf(" Network: %+v\n", net) + fmt.Printf(" GetCIDRBlock(): %s\n", net.GetCIDRBlock()) + fmt.Printf(" GetSubnets(): %v\n", net.GetSubnets()) + + networkDetails, err := netutil.NewNetworkDetails(cidrBlock) + fmt.Printf("\n NetworkDetails: %+v\n", networkDetails) + fmt.Printf(" GetCIDRBlock(): %s\n", networkDetails.GetCIDRBlock()) + fmt.Printf(" GetNetworkAddress(): %s\n", networkDetails.GetNetworkAddress()) + fmt.Printf(" GetBroadcastAddress(): %s\n", networkDetails.GetBroadcastAddress()) + fmt.Printf(" GetPrefix(): %d\n", networkDetails.GetPrefix()) + fmt.Printf(" GetNetmask(): %s\n", networkDetails.GetNetmask()) + fmt.Printf(" GetHostCapacity(): %d\n", networkDetails.GetHostCapacity()) + fmt.Printf(" GetSubnets(): %v\n", networkDetails.GetSubnets()) + + /////////////////////////////////////////////////////////////////////////////////////////////////// + fmt.Println("\n(Under development) Network template example") + // Define the base network + baseNetwork := netutil.Network{ + CIDRBlock: "10.0.0.0/16", + Subnets: []netutil.Network{ + { + // Define a VPC Network + CIDRBlock: "10.0.1.0/24", + Subnets: []netutil.Network{ + { + // Define a Subnetwork within the VPC + CIDRBlock: "10.0.1.0/28", + }, + { + // Another Subnetwork within the VPC + CIDRBlock: "10.0.1.16/28", + }, + }, + }, + { + // Another VPC Network + CIDRBlock: "10.0.2.0/24", + Subnets: []netutil.Network{ + { + // Subnetwork within the second VPC + CIDRBlock: "10.0.2.0/28", + }, + }, + }, + }, + } + fmt.Println("Base Network CIDR:", baseNetwork.CIDRBlock) + for i, vpc := range baseNetwork.Subnets { + fmt.Printf("VPC Network %d CIDR: %s\n", i+1, vpc.CIDRBlock) + for j, subnet := range vpc.Subnets { + fmt.Printf("\tSubnetwork %d CIDR: %s\n", j+1, subnet.CIDRBlock) + } + } + + /////////////////////////////////////////////////////////////////////////////////////////////////// + fmt.Println("\n(Under development) Design multi-cloud network") + jsonData := `{ + "baseNetwork": { + "name": "BaseNetwork1", + "cidrBlock": "10.0.0.0/16", + "subnets": [ + { + "name": "CloudNetwork1", + "cidrBlock": "10.0.1.0/24", + "subnets": [ + {"name": "Subnet1", "cidrBlock": "10.0.1.0/26"}, + {"name": "Subnet2", "cidrBlock": "10.0.1.64/26"}, + {"name": "Subnet3", "cidrBlock": "10.0.1.128/26"}, + {"name": "Subnet4", "cidrBlock": "10.0.1.192/26"} + ] + } + ] + } + }` + + var config netutil.NetworkConfig + err = json.Unmarshal([]byte(jsonData), &config) + if err != nil { + log.Fatalf("Error occurred during unmarshaling. Error: %s", err.Error()) + } + + prettyConfig, err := json.MarshalIndent(config, "", " ") + if err != nil { + log.Fatalf("marshaling error: %s", err) + } + fmt.Printf("[Configuration]\n%s", string(prettyConfig)) }