Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VendorConfigManager implementations for dell, supermicro and asrockrack #12

Merged
merged 9 commits into from
Apr 15, 2024
112 changes: 112 additions & 0 deletions config/asrockrack.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package config

import (
"encoding/xml"
"strings"
)

type asrockrackVendorConfig struct {
ConfigFormat string
ConfigData *asrockrackConfig
}

type asrockrackConfig struct {
BiosCfg *asrockrackBiosCfg `xml:"BiosCfg"`
}

type asrockrackBiosCfg struct {
XMLName xml.Name `xml:"BiosCfg"`
Menus []*asrockrackBiosCfgMenu `xml:"Menu"`
}

type asrockrackBiosCfgMenu struct {
XMLName xml.Name `xml:"Menu"`
Name string `xml:"name,attr"`
Settings []*asrockrackBiosCfgSetting `xml:"Setting"`
Menus []*asrockrackBiosCfgMenu `xml:"Menu"`
}

type asrockrackBiosCfgSetting struct {
XMLName xml.Name `xml:"Setting"`
Name string `xml:"Name,attr"`
Order string `xml:"order,attr"`
SelectedOption string `xml:"selectedOption,attr"`
Type string `xml:"type,attr"`
}

func NewAsrockrackVendorConfigManager(configFormat string) (VendorConfigManager, error) {
asrr := &asrockrackVendorConfig{}

switch strings.ToLower(configFormat) {
case "json":
asrr.ConfigFormat = strings.ToLower(configFormat)
default:
return nil, UnknownConfigFormatError(strings.ToLower(configFormat))
}

asrr.ConfigData = &asrockrackConfig{
BiosCfg: &asrockrackBiosCfg{},
}

return asrr, nil
}

// FindMenu locates an existing asrockrackBiosCfgMenu if one exists in the ConfigData, if not
// it creates one and returns a pointer to that.
func (cm *asrockrackVendorConfig) FindMenu(menuName string) (m *asrockrackBiosCfgMenu) {
for _, m = range cm.ConfigData.BiosCfg.Menus {
if m.Name == menuName {
return
}
}

m.Name = menuName

cm.ConfigData.BiosCfg.Menus = append(cm.ConfigData.BiosCfg.Menus, m)

return
}

// FindMenuSetting locates an existing asrockrackBiosCfgSetting if one exists in the
// ConfigData, if not it creates one and returns a pointer to that.
func (cm *asrockrackVendorConfig) FindMenuSetting(m *asrockrackBiosCfgMenu, name string) (s *asrockrackBiosCfgSetting) {
for _, s = range m.Settings {
if s.Name == name {
return
}
}

s.Name = name

m.Settings = append(m.Settings, s)

return
}

// TODO(jwb) How do we handle the random nature of sub menus here.. we could make the user pass the explicit pointer to a menu struct, or..
func (cm *asrockrackVendorConfig) Raw(name, value string, menuPath []string) {
}

func (cm *asrockrackVendorConfig) Marshal() (string, error) {
switch strings.ToLower(cm.ConfigFormat) {
case "xml":
x, err := xml.Marshal(cm.ConfigData)
if err != nil {
return "", err
}

return string(x), nil
default:
return "", UnknownConfigFormatError(strings.ToLower(cm.ConfigFormat))
}
}

// Generic config options

func (cm *asrockrackVendorConfig) EnableTPM() {
// Unimplemented
}

func (cm *asrockrackVendorConfig) EnableSRIOV() {
// Unimplemented
}
126 changes: 126 additions & 0 deletions config/dell.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package config

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

type dellVendorConfig struct {
ConfigFormat string
ConfigData *dellConfig
}

type dellConfig struct {
SystemConfiguration *dellSystemConfiguration `xml:"SystemConfiguration" json:"SystemConfiguration"`
}

type dellSystemConfiguration struct {
XMLName xml.Name `xml:"SystemConfiguration"`
Model string `xml:"Model,attr" json:"Model"`
Comments []string `xml:"Comments>Comment,omitempty" json:"Comments,omitempty" `
ServiceTag string `xml:"ServiceTag,attr" json:"ServiceTag"`
TimeStamp string `xml:"TimeStamp,attr" json:"TimeStamp"`
Components []*dellComponent `xml:"Component" json:"Components"`
}

type dellComponent struct {
XMLName xml.Name `xml:"Component"`
FQDD string `xml:"FQDD,attr" json:"FQDD"`
Attributes []*dellComponentAttribute `xml:"Attribute" json:"Attributes"`
}

type dellComponentAttribute struct {
XMLName xml.Name `xml:"Attribute"`
Name string `xml:"Name,attr" json:"Name"`
SetOnImport bool `json:"SetOnImport"`
Comment string `json:"Comment"`
Value string `xml:",chardata" json:"Value"`
}

func NewDellVendorConfigManager(configFormat string) (VendorConfigManager, error) {
dell := &dellVendorConfig{}

switch strings.ToLower(configFormat) {
case "xml", "json":
dell.ConfigFormat = strings.ToLower(configFormat)
default:
return nil, UnknownConfigFormatError(strings.ToLower(configFormat))
}

dell.ConfigData = &dellConfig{
SystemConfiguration: &dellSystemConfiguration{},
}

return dell, nil
}

// FindComponent locates an existing DellComponent if one exists in the ConfigData, if not
// it creates one and returns a pointer to that.
func (cm *dellVendorConfig) FindComponent(fqdd string) (c *dellComponent) {
for _, c = range cm.ConfigData.SystemConfiguration.Components {
if c.FQDD == fqdd {
return
}
}

c.FQDD = fqdd

cm.ConfigData.SystemConfiguration.Components = append(cm.ConfigData.SystemConfiguration.Components, c)

return
}

// FindComponentAttribute locates an existing DellComponentAttribute if one exists in the
// ConfigData, if not it creates one and returns a pointer to that.
func (cm *dellVendorConfig) FindComponentAttribute(c *dellComponent, name string) (a *dellComponentAttribute) {
for _, a = range c.Attributes {
if a.Name == name {
return
}
}

a.Name = name

c.Attributes = append(c.Attributes, a)

return
}

func (cm *dellVendorConfig) Raw(name, value string, menuPath []string) {
c := cm.FindComponent(menuPath[0])
attr := cm.FindComponentAttribute(c, name)
attr.Value = value
}

func (cm *dellVendorConfig) Marshal() (string, error) {
switch strings.ToLower(cm.ConfigFormat) {
case "xml":
x, err := xml.Marshal(cm.ConfigData)
if err != nil {
return "", err
}

return string(x), nil
case "json":
x, err := json.Marshal(cm.ConfigData)
if err != nil {
return "", err
}

return string(x), nil
default:
return "", UnknownConfigFormatError(strings.ToLower(cm.ConfigFormat))
}
}

// Generic config options

func (cm *dellVendorConfig) EnableTPM() {
cm.Raw("EnableTPM", "Enabled", []string{"BIOS.Setup.1-1"})
}

func (cm *dellVendorConfig) EnableSRIOV() {
// TODO(jwb) How do we want to handle enabling this for different NICs
cm.Raw("VirtualizationMode", "SRIOV", []string{"NIC.Slot.3-1-1"})
}
17 changes: 17 additions & 0 deletions config/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package config

import (
"errors"
"fmt"
)

var errUnknownConfigFormat = errors.New("unknown config format")
var errUnknownVendor = errors.New("unknown/unsupported vendor")

func UnknownConfigFormatError(format string) error {
return fmt.Errorf("unknown config format %w : %s", errUnknownConfigFormat, format)
}

func UnknownVendorError(vendorName string) error {
return fmt.Errorf("unknown/unsupported vendor %w : %s", errUnknownVendor, vendorName)
}
28 changes: 28 additions & 0 deletions config/interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package config

import (
"strings"

"github.com/bmc-toolbox/common"
)

type VendorConfigManager interface {
EnableTPM()
EnableSRIOV()

Raw(name, value string, menuPath []string)
Marshal() (string, error)
}

func NewVendorConfigManager(configFormat, vendorName string) (VendorConfigManager, error) {
switch strings.ToLower(vendorName) {
case common.VendorDell:
return NewDellVendorConfigManager(configFormat)
case common.VendorSupermicro:
return NewSupermicroVendorConfigManager(configFormat)
case common.VendorAsrockrack:
return NewAsrockrackVendorConfigManager(configFormat)
default:
return nil, UnknownVendorError(strings.ToLower(vendorName))
}
}
Loading
Loading