Skip to content

Commit

Permalink
Merge pull request #79 from Cray-HPE/import-improvements
Browse files Browse the repository at this point in the history
Reconcile and import improvements
  • Loading branch information
rsjostrand-hpe authored Jun 27, 2023
2 parents ac1f4b2 + 2ecf4ca commit 1cf2844
Show file tree
Hide file tree
Showing 20 changed files with 557 additions and 111 deletions.
6 changes: 4 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ endif
validate-hardware-type-schemas \
generate \
generate-go \
generate-swagger
generate-swagger \
license

all: bin

Expand Down Expand Up @@ -138,6 +139,7 @@ validate-hardware-type-schemas:

unittest: bin
GOOS=$(GOOS) GOARCH=$(GOARCH) go test -cover \
github.com/Cray-HPE/cani/internal/provider/csm \
github.com/Cray-HPE/cani/internal/provider/csm/validate \
github.com/Cray-HPE/cani/internal/provider/csm/validate/common

Expand Down Expand Up @@ -191,7 +193,7 @@ generate-swagger-hsm-client: bin/swagger-codegen-cli.jar
generate: generate-swagger-sls-client generate-swagger-hsm-client generate-go

license:
docker run -it --rm -v $(pwd):/github/workspace artifactory.algol60.net/csm-docker/stable/license-checker .github/workflows/ cmd/ internal pkg/hardwaretypes pkg/xname --fix
docker run -it --rm -v ${PWD}:/github/workspace artifactory.algol60.net/csm-docker/stable/license-checker .github/workflows/ cmd/ internal pkg/hardwaretypes pkg/xname --fix

# Jenkins doesn't have java installed, so the generate target fails to run
bin:
Expand Down
17 changes: 17 additions & 0 deletions internal/inventory/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,23 @@ type Hardware struct {
LocationOrdinal *int
}

func NewHardwareFromBuildOut(hardwareBuildOut hardwaretypes.HardwareBuildOut, status HardwareStatus) Hardware {
locationOrdinal := hardwareBuildOut.OrdinalPath[len(hardwareBuildOut.OrdinalPath)-1]

return Hardware{
ID: hardwareBuildOut.ID,
Parent: hardwareBuildOut.ParentID,
Type: hardwareBuildOut.DeviceType.HardwareType,
DeviceTypeSlug: hardwareBuildOut.DeviceType.Slug,
Vendor: hardwareBuildOut.DeviceType.Manufacturer,
Model: hardwareBuildOut.DeviceType.Model,

LocationOrdinal: &locationOrdinal,

Status: status,
}
}

// HardwareStatus is the current state of the hardware
// Using a status allows for the hardware to be tracked through its lifecycle
// and allows for historical tracking of the hardware even if it is replaced or removed
Expand Down
2 changes: 1 addition & 1 deletion internal/provider/csm/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -689,7 +689,7 @@ func (csm *CSM) Import(ctx context.Context, datastore inventory.Datastore) error

// Commit changes!
if err := datastore.Merge(tempDatastore); err != nil {
return errors.Join(fmt.Errorf("failed to merge temporary datastore with actual datastore", err))
return errors.Join(fmt.Errorf("failed to merge temporary datastore with actual datastore"), err)
}

return datastore.Flush()
Expand Down
37 changes: 27 additions & 10 deletions internal/provider/csm/reconcile.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func (csm *CSM) Reconcile(ctx context.Context, datastore inventory.Datastore) (e
//
// Build up the expected SLS state
//
expectedSLSState, hardwareMapping, err := BuildExpectedHardwareState(datastore)
expectedSLSState, hardwareMapping, err := BuildExpectedHardwareState(*csm.hardwareLibrary, datastore, currentSLSState.Networks)
if err != nil {
return errors.Join(
fmt.Errorf("failed to build expected SLS state"),
Expand Down Expand Up @@ -215,7 +215,7 @@ func (csm *CSM) Reconcile(ctx context.Context, datastore inventory.Datastore) (e

// Update existing hardware
for _, hardwarePair := range hardwareWithDifferingValues {
updatedHardware := hardwarePair.HardwareB
updatedHardware := hardwarePair.HardwareA // A is expected, B is actual
log.Info().Str("xname", updatedHardware.Xname).Msg("Updating")
// Put into transaction log with old and new value
// TODO
Expand Down Expand Up @@ -353,17 +353,34 @@ func buildHardwareString(hardware sls_client.Hardware) (string, error) {

var tokens []string
tokens = append(tokens, fmt.Sprintf("Type: %s", hardware.TypeString))
tokens = append(tokens, fmt.Sprintf("Class: %s", hardware.Class))

switch hardware.TypeString {
// case xnametypes.Cabinet:
// // If we don't know how to pretty print it, lets just do the raw JSON
// extraPropertiesRaw, err := json.Marshal(hardware.ExtraProperties)
// if err != nil {
// return "", err
// }
// tokens = append(tokens, string(extraPropertiesRaw))
case xnametypes.Cabinet:
// If we don't know how to pretty print it, lets just do the raw JSON
// extraPropertiesRaw, err := json.Marshal(hardware.ExtraProperties)
// if err != nil {
// return "", err
// }
// tokens = append(tokens, string(extraPropertiesRaw))
if extraProperties, ok := extraPropertiesRaw.(sls_client.HardwareExtraPropertiesCabinet); ok {
if extraProperties.Model != "" {
tokens = append(tokens, fmt.Sprintf("Model: %s", extraProperties.Model))
}
if extraProperties.DHCPRelaySwitches != nil {
tokens = append(tokens, fmt.Sprintf("DHCPRelaySwitches: %s", strings.Join(extraProperties.DHCPRelaySwitches, ",")))
}
if extraProperties.Networks != nil {
networksRaw, err := json.Marshal(extraProperties.Networks)
if err != nil {
return "", err
}
tokens = append(tokens, fmt.Sprintf("Networks: %s", networksRaw))
}
}

case xnametypes.Chassis:
// Nothing to do
// Nothing to do
case xnametypes.ChassisBMC:
// Nothing to do
case xnametypes.CabinetPDUController:
Expand Down
6 changes: 0 additions & 6 deletions internal/provider/csm/sls/diff_sls.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,13 +167,7 @@ func stripIpInformationFromHardware(extraPropertiesRaw interface{}) interface{}
ep.CaniId = ""
ep.CaniSlsSchemaVersion = ""
ep.CaniLastModified = ""

ep.Networks = nil
ep.Model = ""
// TODO deal with this at somepoint
// if cabinetKind := csi.CabinetKind(ep.Model); cabinetKind.IsModel() {
// ep.Model = ""
// }
return ep
case sls_client.HardwareExtraPropertiesCduMgmtSwitch:
ep.CaniId = ""
Expand Down
188 changes: 107 additions & 81 deletions internal/provider/csm/sls_state_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,24 +34,49 @@ import (
"github.com/Cray-HPE/cani/internal/provider/csm/sls"
"github.com/Cray-HPE/cani/pkg/hardwaretypes"
sls_client "github.com/Cray-HPE/cani/pkg/sls-client"
"github.com/google/uuid"
"github.com/rs/zerolog/log"
)

// func GetProviderProperties[T any](hardware inventory.Hardware) (*T, error) {
// providerPropertiesRaw, ok := hardware.ProviderProperties["csm"]
// if !ok {
// return nil, nil // This should be ok, as its possible as not all hardware inventory items may have CSM specific data
// }
func DetermineHardwareClass(hardware inventory.Hardware, data inventory.Inventory, hardwareTypeLibrary hardwaretypes.Library) (sls_client.HardwareClass, error) {
currentHardwareID := hardware.ID
for currentHardwareID != uuid.Nil {
currentHardware, exists := data.Hardware[currentHardwareID]
if !exists {
return "", errors.Join(
fmt.Errorf("unable to find ancestor (%s) of (%s)", currentHardwareID, hardware.ID),
)
}

deviceType, exists := hardwareTypeLibrary.DeviceTypes[currentHardware.DeviceTypeSlug]
if !exists {
return "", errors.Join(
fmt.Errorf("unable to find device type (%s) for (%s)", currentHardware.DeviceTypeSlug, currentHardwareID),
)
}

if deviceType.ProviderDefaults != nil && deviceType.ProviderDefaults.CSM != nil && deviceType.ProviderDefaults.CSM.Class != nil {
classRaw := *deviceType.ProviderDefaults.CSM.Class
switch classRaw {
case "River":
return sls_client.HardwareClassRiver, nil
case "Mountain":
return sls_client.HardwareClassMountain, nil
case "Hill":
return sls_client.HardwareClassHill, nil
default:
return "", fmt.Errorf("encountered unknown CSM hardware class (%s)", classRaw)
}
}

// var result T
// if err := mapstructure.Decode(providerPropertiesRaw, &result); err != nil {
// return nil, err
// }
// Go the parent node next
currentHardwareID = currentHardware.Parent
}

// return &providerProperties, nil
// }
return "", fmt.Errorf("unable to determine CSM Class of (%s)", hardware.ID)
}

func BuildExpectedHardwareState(datastore inventory.Datastore) (sls_client.SlsState, map[string]inventory.Hardware, error) {
func BuildExpectedHardwareState(hardwareTypeLibrary hardwaretypes.Library, datastore inventory.Datastore, slsNetworks map[string]sls_client.Network) (sls_client.SlsState, map[string]inventory.Hardware, error) {
// Retrieve the CANI inventory data
data, err := datastore.List()
if err != nil {
Expand All @@ -68,6 +93,11 @@ func BuildExpectedHardwareState(datastore inventory.Datastore) (sls_client.SlsSt
// Iterate over the CANI inventory data to build SLS data
allHardware := map[string]sls_client.Hardware{}
for _, cHardware := range data.Hardware {
// Skip systems
if cHardware.Type == hardwaretypes.System {
continue
}

//
// Build the SLS hardware representation
//
Expand All @@ -80,7 +110,15 @@ func BuildExpectedHardwareState(datastore inventory.Datastore) (sls_client.SlsSt
)
}

hardware, err := BuildSLSHardware(cHardware, locationPath)
slsClass, err := DetermineHardwareClass(cHardware, data, hardwareTypeLibrary)
if err != nil {
return sls_client.SlsState{}, nil, errors.Join(
fmt.Errorf("failed to determine SLS class of hardware (%s)", cHardware.ID),
err,
)
}

hardware, err := BuildSLSHardware(cHardware, locationPath, slsClass, slsNetworks)
// if err != nil && ignoreUnknownCANUHardwareArchitectures && strings.Contains(err.Error(), "unknown architecture type") {
// log.Printf("WARNING %s", err.Error())
// } else if err != nil {
Expand Down Expand Up @@ -150,42 +188,13 @@ func BuildExpectedHardwareState(datastore inventory.Datastore) (sls_client.SlsSt

}

// Generate Cabinet Objects
// TODO this will be handled in the code above ^
// for cabinetKind, cabinets := range cabinetLookup {
// for _, cabinet := range cabinets {
// class, err := cabinetKind.Class()
// if err != nil {
// panic(err)
// }

// extraProperties := sls_client.ComptypeCabinet{
// Networks: map[string]map[string]sls_client.CabinetNetworks{}, // TODO this should be outright removed. MEDS and KEA no longer look here for network info, but MEDS still needs this key to exist.
// }

// if cabinetKind.IsModel() {
// extraProperties.Model = string(cabinetKind)
// }

// hardware := sls_client.NewGenericHardware(cabinet, class, extraProperties)

// // Verify new hardware
// if _, present := allHardware[hardware.Xname]; present {
// err := fmt.Errorf("found duplicate xname %v", hardware.Xname)
// panic(err)
// }

// allHardware[hardware.Xname] = hardware
// }
// }

// Build up and the SLS state
return sls_client.SlsState{
Hardware: allHardware,
}, hardwareMapping, nil
}

func BuildSLSHardware(cHardware inventory.Hardware, locationPath inventory.LocationPath) (sls_client.Hardware, error) {
func BuildSLSHardware(cHardware inventory.Hardware, locationPath inventory.LocationPath, class sls_client.HardwareClass, slsNetworks map[string]sls_client.Network) (sls_client.Hardware, error) {
log.Debug().Stringer("locationPath", locationPath).Msg("LocationPath")

// Get the physical location for the hardware
Expand All @@ -194,26 +203,78 @@ func BuildSLSHardware(cHardware inventory.Hardware, locationPath inventory.Locat
if err != nil {
return sls_client.Hardware{}, err
} else if xname == nil {
// This means that this piece of the hardware inventory can't be represented in SLS, so just skip it
// This means that this piece of the hardware inventory can't be represented in SLS due to no xname, so just skip it
return sls_client.Hardware{}, nil
}

// Get the class of the piece of hardware
// Generally this will match the class of the containing cabinet, the exception is river hardware within a EX2500 cabinet.
// TODO
var extraProperties interface{}
class := sls_client.HardwareClassMountain

switch cHardware.Type {
case hardwaretypes.Cabinet:
var cabinetExtraProperties sls_client.HardwareExtraPropertiesCabinet

//
// Apply CANI Metadata
//
cabinetExtraProperties.CaniId = cHardware.ID.String()
cabinetExtraProperties.CaniSlsSchemaVersion = "v1alpha1"
cabinetExtraProperties.CaniLastModified = time.Now().UTC().String()

// TODO need cabinet metadata
//
// Build cabinet metadata
//
cabinetExtraProperties.Networks = map[string]map[string]sls_client.HardwareExtraPropertiesCabinetNetworks{
"cn": map[string]sls_client.HardwareExtraPropertiesCabinetNetworks{},
}

// Determine which SLS network contains the cabinet subnet
hmnNetworkName := "HMN_MTN"
nmnNetworkName := "NMN_MTN"
if class == sls_client.HardwareClassRiver {
hmnNetworkName = "HMN_RVR"
nmnNetworkName = "NMN_RVR"
}

// Determine the subnet name, should be the same between the HMN_* and NMN_* networks
subnetName := fmt.Sprintf("cabinet_%d", *cHardware.LocationOrdinal)

// Find cabinet HMN subnet
hmnNetwork, exists := slsNetworks[hmnNetworkName]
if !exists {
return sls_client.Hardware{}, fmt.Errorf("SLS Network (%s) does not exist", hmnNetworkName)
}
for _, subnet := range hmnNetwork.ExtraProperties.Subnets {
if subnet.Name == subnetName {
cabinetExtraProperties.Networks["cn"]["HMN"] = sls_client.HardwareExtraPropertiesCabinetNetworks{
CIDR: subnet.CIDR,
Gateway: subnet.Gateway,
VLan: subnet.VlanID,
}
}
}

// Find cabinet NMN subnet
nmnNetwork, exists := slsNetworks[nmnNetworkName]
if !exists {
return sls_client.Hardware{}, fmt.Errorf("SLS Network (%s) does not exist", nmnNetworkName)
}
for _, subnet := range nmnNetwork.ExtraProperties.Subnets {
if subnet.Name == subnetName {
cabinetExtraProperties.Networks["cn"]["NMN"] = sls_client.HardwareExtraPropertiesCabinetNetworks{
CIDR: subnet.CIDR,
Gateway: subnet.Gateway,
VLan: subnet.VlanID,
}
}
}

if class == sls_client.HardwareClassRiver {
// If this is a river cabinet, we need to make a entry for ncn network.
cabinetExtraProperties.Networks["ncn"] = cabinetExtraProperties.Networks["cn"]
}

extraProperties = cabinetExtraProperties
case hardwaretypes.Chassis:
Expand Down Expand Up @@ -291,41 +352,6 @@ func BuildSLSHardware(cHardware inventory.Hardware, locationPath inventory.Locat
return sls.NewHardware(xname, class, extraProperties), nil
}

// func buildSLSPDUController(location Location) (sls_client.GenericHardware, error) {
// }

// func buildSLSSlingshotHSNSwitch(location Location) (sls_client.GenericHardware, error) {
// }

// func buildSLSCMC(location Location) (sls_client.GenericHardware, error) {
// // TODO what should be done if if the CMC does not have a bmc connection? Ie the Intel CMC that doesn't really exist
// // Right now we are emulating the current behavior of CSI, where the fake CMC exists in SLS and no MgmtSwitchConnector exists.

// }

// // BuildNodeExtraProperties will attempt to build up all of the known extra properties form a Node present in a CCJ.
// // Limiitations the following information is not populated:
// // - Management NCN NID
// // - Application Node Subrole and Alias

// func BuildNodeExtraProperties(topologyNode TopologyNode) (extraProperties sls_client.ComptypeNode, err error) {
// }

// func buildSLSNode(xname) (sls_client.GenericHardware, error) {
// }

// func buildSLSMgmtSwitch(topologyNode TopologyNode, switchAliasesOverrides map[string][]string) (sls_client.GenericHardware, error) {
// }

// func buildSLSMgmtHLSwitch(topologyNode TopologyNode, switchAliasesOverrides map[string][]string) (sls_client.GenericHardware, error) {
// }

// func buildSLSCDUMgmtSwitch(topologyNode TopologyNode, switchAliasesOverrides map[string][]string) (sls_client.GenericHardware, error) {
// }

func BuildSLSMgmtSwitchConnector(hardware sls_client.Hardware, cHardware inventory.Hardware) (sls_client.Hardware, error) {
return sls_client.Hardware{}, nil
}

// func buildSLSChassisBMC(location Location, cl configs.CabinetLookup) (sls_client.GenericHardware, error) {
// }
Loading

0 comments on commit 1cf2844

Please sign in to comment.