Skip to content

Commit

Permalink
Merge pull request #432 from Cray-HPE/MTL-2436-2437-disallow-networki…
Browse files Browse the repository at this point in the history
…ng-dupes

Add VLAN management with overlap and re-use checks
  • Loading branch information
trad511 authored Oct 16, 2024
2 parents ac9f11f + 1d74da9 commit 0202267
Show file tree
Hide file tree
Showing 4 changed files with 341 additions and 24 deletions.
80 changes: 57 additions & 23 deletions pkg/cli/config/initialize/initialize.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ func NewCommand() *cobra.Command {

}

// Remember a loop over a map is random ordered in Go
for name, layout := range internalNetConfigs {
myLayout := layout

Expand All @@ -244,33 +245,66 @@ func NewCommand() *cobra.Command {
"-",
)

// Use CLI values if available, otherwise defaults
if v.IsSet(
fmt.Sprintf(
"%v-bootstrap-vlan",
normalizedName,
),
) {
myLayout.BaseVlan = int16(
v.GetInt(
fmt.Sprintf(
"%v-bootstrap-vlan",
normalizedName,
),
),
)
myLayout.Template.VlanRange[0] = int16(
v.GetInt(
fmt.Sprintf(
"%v-bootstrap-vlan",
normalizedName,
),
),
)
// Use CLI/file input values if available, otherwise defaults
baseVlanName := fmt.Sprintf("%v-bootstrap-vlan", normalizedName)
if v.IsSet(baseVlanName) {
baseVlan := int16(v.GetInt(baseVlanName))
myLayout.BaseVlan = baseVlan
myLayout.Template.VlanRange[0] = baseVlan
} else {
myLayout.BaseVlan = layout.Template.VlanRange[0]
}

// Check VLAN allocations for re-use and overlaps
if len(layout.Template.VlanRange) == 2 {
err := networking.AllocateVlanRange(
myLayout.Template.VlanRange[0],
myLayout.Template.VlanRange[1],
)
if err != nil {
log.Fatalln(
"Unable to allocate VLAN range for",
myLayout.Template.Name,
err,
)
} else {
log.Println(
"Allocating VLANs",
myLayout.Template.Name,
myLayout.Template.VlanRange[0],
myLayout.Template.VlanRange[1],
)
}
} else {
err := networking.AllocateVlan(
myLayout.Template.VlanRange[0],
)
if err != nil {
log.Fatalln(
"Unable to allocate single VLAN for",
myLayout.Template.Name,
myLayout.Template.VlanRange[0],
err,
)
} else {
log.Println(
"Allocating VLAN ",
myLayout.Template.Name,
myLayout.Template.VlanRange[0],
)
}
}

allocated, err := networking.IsVlanAllocated(myLayout.BaseVlan)
if !allocated {
log.Fatalln(
"VLAN for",
layout.Template.Name,
"has not been initialized by defaults or input values:",
err,
)
}

if v.IsSet(
fmt.Sprintf(
"%v-cidr",
Expand Down
2 changes: 1 addition & 1 deletion pkg/cli/config/initialize/sls/networkBuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ Handy Netmask Cheet Sheet

const (
// DefaultMTLVlan is the default MTL Bootstrap Vlan - zero (0) represents untagged.
DefaultMTLVlan = 0
DefaultMTLVlan = 1
// DefaultHMNString is the Default HMN String (bond0.hmn0)
DefaultHMNString = "10.254.0.0/17"
// DefaultHMNVlan is the default HMN Bootstrap Vlan
Expand Down
69 changes: 69 additions & 0 deletions pkg/networking/ipam.go
Original file line number Diff line number Diff line change
Expand Up @@ -595,3 +595,72 @@ var netmasks = map[int]string{
32766: "/17",
65534: "/16",
}

// Basic VLAN management
var vlans = [4096]bool{4095: true}

// IsVlanAllocated takes an int16 and tests if a given VLAN is already allocated and managed.
func IsVlanAllocated(vlan int16) (bool, error) {
if vlan > 4095 || vlan < 0 {
return true, errors.New("VLAN out of range")
}
return vlans[vlan], nil
}

// AllocateVlan takes an int16 and manages a single VLAN.
func AllocateVlan(vlan int16) error {
allocated, err := IsVlanAllocated(vlan)
if allocated {
if err != nil {
return err
}
return errors.New("VLAN already used")
}
vlans[vlan] = true
return nil
}

// FreeVlan takes an int16 and stops managing a single VLAN.
func FreeVlan(vlan int16) error {
vlans[vlan] = false
return nil
}

// AllocateVlanRange takes two int16 and manages a range of VLANs.
func AllocateVlanRange(startvlan, endvlan int16) error {
if startvlan > endvlan {
return errors.New("VLAN range is bad - start is larger than end")
}
// Pre-test all VLANs for previous allocation
hasAllocationErrors := false
allocatedVlans := []int16{}
for vlan := startvlan; vlan <= endvlan; vlan++ {
allocated, err := IsVlanAllocated(vlan)
if err != nil {
return err
}
if allocated {
hasAllocationErrors = true
allocatedVlans = append(allocatedVlans, vlan)
}
}
if hasAllocationErrors {
return fmt.Errorf("VLANs already used: %v", allocatedVlans)
}

for vlan := startvlan; vlan <= endvlan; vlan++ {
AllocateVlan(vlan)
}
return nil
}

// FreeVlanRange takes two int16 and stops managing a range of VLANs.
func FreeVlanRange(startvlan, endvlan int16) error {
if startvlan > endvlan {
return errors.New("VLAN range is bad - start is larger than end")
}
for vlan := startvlan; vlan <= endvlan; vlan++ {
FreeVlan(vlan)
}
return nil
}
214 changes: 214 additions & 0 deletions pkg/networking/ipam_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
/*
MIT License
(C) Copyright 2022-2024 Hewlett Packard Enterprise Development LP
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/

package networking

import (
"errors"
"testing"

"github.com/stretchr/testify/suite"
)

type NetworksTestSuite struct {
suite.Suite
}

func (suite *NetworksTestSuite) TestIsVlanAllocatedBadVlans() {
tests := []struct {
vlan int16
expectedBool bool
expectedError error
}{
{
// VLAN out of range too small
vlan: -1,
expectedBool: true,
expectedError: errors.New("VLAN out of range"),
},
{
// VLAN out of range too big
vlan: 4096,
expectedBool: true,
expectedError: errors.New("VLAN out of range"),
},
}

for _, test := range tests {
val, err := IsVlanAllocated(test.vlan)
suite.Equal(
val,
test.expectedBool,
)
suite.Equal(
test.expectedError,
err,
)
}
}

func (suite *NetworksTestSuite) TestAllocateVlanBadVlans() {
tests := []struct {
vlan int16
expectedError error
}{
{
// VLAN out of range too small
vlan: -1,
expectedError: errors.New("VLAN out of range"),
},
{
// VLAN out of range too big
vlan: 4096,
expectedError: errors.New("VLAN out of range"),
},
{
// VLAN 7 not allocated and this should work
vlan: 7,
expectedError: nil,
},
}

for _, test := range tests {
err := AllocateVlan(test.vlan)
suite.Equal(
test.expectedError,
err,
)
}
}

func (suite *NetworksTestSuite) TestVlanAllocate() {
// Allocate VLAN 2 the first time should be success
var vlan int16 = 2
suite.Equal(
nil,
AllocateVlan(vlan),
)
//Allocate VLAN 2 the 2nd time should fail as it's already used
suite.Equal(
errors.New("VLAN already used"),
AllocateVlan(vlan),
)
}

func (suite *NetworksTestSuite) TestVlanAllocateRange() {
// Bad start and end range
suite.Equal(
errors.New("VLAN out of range"),
AllocateVlanRange(4092, 4099),
)

var startVlan int16 = 200
var endVlan int16 = 299

// Bad start and end range
suite.Equal(
errors.New("VLAN range is bad - start is larger than end"),
AllocateVlanRange(endVlan, startVlan),
)

// VLANs already in use in the range cannot be allocated, including ends
AllocateVlan(200)
AllocateVlan(207)
AllocateVlan(213)
AllocateVlan(299)
suite.Equal(
errors.New("VLANs already used: [200 207 213 299]"),
AllocateVlanRange(startVlan, endVlan),
)

// Successfully allocate a range of VLANs
FreeVlan(200)
FreeVlan(207)
FreeVlan(213)
FreeVlan(299)
suite.Equal(
nil,
AllocateVlanRange(startVlan, endVlan),
)
}

func (suite *NetworksTestSuite) TestVlanFree() {
// Allocate VLAN 4
var vlan int16 = 4
suite.Equal(
nil,
AllocateVlan(vlan),
)

// Deallocate VLAN 4 the 1st time should succeed
suite.Equal(
nil,
FreeVlan(vlan),
)

// Deallocate VLAN 4 the 2nd time should succeed
suite.Equal(
nil,
FreeVlan(vlan),
)
}

func (suite *NetworksTestSuite) TestVlanFreeRange() {
var startVlan int16 = 400
var endVlan int16 = 499
suite.Equal(
errors.New("VLAN range is bad - start is larger than end"),
FreeVlanRange(endVlan, startVlan),
)

AllocateVlan(startVlan)
AllocateVlan(endVlan)
// Deallocate the range the first time - should succeed
suite.Equal(
nil,
FreeVlanRange(startVlan, endVlan),
)

startAllocation, _ := IsVlanAllocated(startVlan)
suite.Equal(
false,
startAllocation,
)

endAllocation, _ := IsVlanAllocated(endVlan)
suite.Equal(
false,
endAllocation,
)

// Deallocate the range the second time - should still succeed
suite.Equal(
nil,
FreeVlanRange(startVlan, endVlan),
)

}
func TestNetworksTestSuite(t *testing.T) {
suite.Run(
t,
new(NetworksTestSuite),
)
}

0 comments on commit 0202267

Please sign in to comment.