Skip to content

Commit

Permalink
feat: add extended hardware information to Server and ServerClass CRDs
Browse files Browse the repository at this point in the history
This is final part of #735, previous part in #823.

This imports all remaining changes with some fixups minus the webhook
changes.

This change adds detailed hardware information to the Server CRD.
Hardware info is extracted by the agent from SMBIOS.
The ServerClass CRD is also updated so more precise qualifiers can be used.

Co-authored-by: Gerard de Leeuw <[email protected]>
Signed-off-by: Gerard de Leeuw <[email protected]>
Signed-off-by: Andrey Smirnov <[email protected]>
  • Loading branch information
smira and lion7 committed Apr 15, 2022
1 parent e4bb416 commit 7c7a377
Show file tree
Hide file tree
Showing 42 changed files with 1,848 additions and 451 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import (

infrav1 "github.com/talos-systems/sidero/app/caps-controller-manager/api/v1alpha3"
"github.com/talos-systems/sidero/app/caps-controller-manager/pkg/constants"
metalv1 "github.com/talos-systems/sidero/app/sidero-controller-manager/api/v1alpha1"
metalv1 "github.com/talos-systems/sidero/app/sidero-controller-manager/api/v1alpha2"
)

var ErrNoServersInServerClass = errors.New("no servers available in serverclass")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/controller"

infrav1 "github.com/talos-systems/sidero/app/caps-controller-manager/api/v1alpha3"
metalv1 "github.com/talos-systems/sidero/app/sidero-controller-manager/api/v1alpha1"
metalv1 "github.com/talos-systems/sidero/app/sidero-controller-manager/api/v1alpha2"
)

// ServerBindingReconciler reconciles a ServerBinding object.
Expand Down
4 changes: 2 additions & 2 deletions app/caps-controller-manager/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (
infrav1alpha2 "github.com/talos-systems/sidero/app/caps-controller-manager/api/v1alpha2"
infrav1alpha3 "github.com/talos-systems/sidero/app/caps-controller-manager/api/v1alpha3"
"github.com/talos-systems/sidero/app/caps-controller-manager/controllers"
metalv1alpha1 "github.com/talos-systems/sidero/app/sidero-controller-manager/api/v1alpha1"
metalv1 "github.com/talos-systems/sidero/app/sidero-controller-manager/api/v1alpha2"
// +kubebuilder:scaffold:imports
)

Expand All @@ -43,7 +43,7 @@ func init() {
_ = capiv1.AddToScheme(scheme)
_ = infrav1alpha2.AddToScheme(scheme)
_ = infrav1alpha3.AddToScheme(scheme)
_ = metalv1alpha1.AddToScheme(scheme)
_ = metalv1.AddToScheme(scheme)
// +kubebuilder:scaffold:scheme
}

Expand Down
243 changes: 196 additions & 47 deletions app/sidero-controller-manager/cmd/agent/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import (
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"

metalv1 "github.com/talos-systems/sidero/app/sidero-controller-manager/api/v1alpha1"
metalv1 "github.com/talos-systems/sidero/app/sidero-controller-manager/api/v1alpha2"
"github.com/talos-systems/sidero/app/sidero-controller-manager/internal/api"
"github.com/talos-systems/sidero/app/sidero-controller-manager/internal/power/ipmi"
"github.com/talos-systems/sidero/app/sidero-controller-manager/pkg/constants"
Expand Down Expand Up @@ -94,25 +94,19 @@ func setup() error {
}

func create(ctx context.Context, client api.AgentClient, s *smbios.SMBIOS) (*api.CreateServerResponse, error) {
uuid, err := s.SystemInformation().UUID()
disks, err := disk.List()
if err != nil {
return nil, err
log.Printf("encountered error fetching disks: %q", err)
}

interfaces, err := net.Interfaces()
if err != nil {
log.Printf("encountered error fetching network interfaces: %q", err)
}

req := &api.CreateServerRequest{
SystemInformation: &api.SystemInformation{
Uuid: uuid.String(),
Manufacturer: s.SystemInformation().Manufacturer(),
ProductName: s.SystemInformation().ProductName(),
Version: s.SystemInformation().Version(),
SerialNumber: s.SystemInformation().SerialNumber(),
SkuNumber: s.SystemInformation().SKUNumber(),
Family: s.SystemInformation().Family(),
},
Cpu: &api.CPU{
Manufacturer: s.ProcessorInformation().ProcessorManufacturer(),
Version: s.ProcessorInformation().ProcessorVersion(),
},
Hardware: MapHardwareInformation(s, disks, interfaces),
Hostname: "",
}

hostname, err := os.Hostname()
Expand Down Expand Up @@ -140,16 +134,11 @@ func create(ctx context.Context, client api.AgentClient, s *smbios.SMBIOS) (*api
}

func wipe(ctx context.Context, client api.AgentClient, s *smbios.SMBIOS) error {
uuid, err := s.SystemInformation().UUID()
if err != nil {
return err
}

return retry.Constant(5*time.Minute, retry.WithUnits(30*time.Second), retry.WithErrorLogging(true)).Retry(func() error {
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()

_, err = client.MarkServerAsWiped(ctx, &api.MarkServerAsWipedRequest{Uuid: uuid.String()})
_, err := client.MarkServerAsWiped(ctx, &api.MarkServerAsWipedRequest{Uuid: s.SystemInformation.UUID})
if err != nil {
return retry.ExpectedError(err)
}
Expand All @@ -159,11 +148,6 @@ func wipe(ctx context.Context, client api.AgentClient, s *smbios.SMBIOS) error {
}

func reconcileIPs(ctx context.Context, client api.AgentClient, s *smbios.SMBIOS, ips []net.IP) error {
uuid, err := s.SystemInformation().UUID()
if err != nil {
return err
}

addresses := make([]*api.Address, len(ips))
for i := range addresses {
addresses[i] = &api.Address{
Expand All @@ -176,8 +160,8 @@ func reconcileIPs(ctx context.Context, client api.AgentClient, s *smbios.SMBIOS,
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()

_, err = client.ReconcileServerAddresses(ctx, &api.ReconcileServerAddressesRequest{
Uuid: uuid.String(),
_, err := client.ReconcileServerAddresses(ctx, &api.ReconcileServerAddressesRequest{
Uuid: s.SystemInformation.UUID,
Address: addresses,
})
if err != nil {
Expand Down Expand Up @@ -281,11 +265,6 @@ func mainFunc() error {
shutdown(err)
}

uuid, err := s.SystemInformation().UUID()
if err != nil {
shutdown(err)
}

var (
eg errgroup.Group
wg sync.WaitGroup
Expand All @@ -305,7 +284,7 @@ func mainFunc() error {
for {
callCtx, cancel := context.WithTimeout(ctx, heartbeatInterval)

if _, err := client.Heartbeat(callCtx, &api.HeartbeatRequest{Uuid: uuid.String()}); err != nil {
if _, err := client.Heartbeat(callCtx, &api.HeartbeatRequest{Uuid: s.SystemInformation.UUID}); err != nil {
log.Printf("Failed to send wipe heartbeat %s", err)
}

Expand Down Expand Up @@ -396,11 +375,6 @@ func main() {
}

func attemptBMCIP(ctx context.Context, client api.AgentClient, s *smbios.SMBIOS) error {
uuid, err := s.SystemInformation().UUID()
if err != nil {
return err
}

bmcInfo := &api.BMCInfo{}

// Create "open" client
Expand Down Expand Up @@ -442,7 +416,7 @@ func attemptBMCIP(ctx context.Context, client api.AgentClient, s *smbios.SMBIOS)
_, err = client.UpdateBMCInfo(
ctx,
&api.UpdateBMCInfoRequest{
Uuid: uuid.String(),
Uuid: s.SystemInformation.UUID,
BmcInfo: bmcInfo,
},
)
Expand All @@ -458,11 +432,6 @@ func attemptBMCIP(ctx context.Context, client api.AgentClient, s *smbios.SMBIOS)
}

func attemptBMCUserSetup(ctx context.Context, client api.AgentClient, s *smbios.SMBIOS) error {
uuid, err := s.SystemInformation().UUID()
if err != nil {
return err
}

bmcInfo := &api.BMCInfo{}

// Create "open" client
Expand Down Expand Up @@ -575,7 +544,7 @@ func attemptBMCUserSetup(ctx context.Context, client api.AgentClient, s *smbios.
_, err = client.UpdateBMCInfo(
ctx,
&api.UpdateBMCInfoRequest{
Uuid: uuid.String(),
Uuid: s.SystemInformation.UUID,
BmcInfo: bmcInfo,
},
)
Expand Down Expand Up @@ -609,3 +578,183 @@ func genPass16() (string, error) {

return string(b), nil
}

func MapHardwareInformation(s *smbios.SMBIOS, disks []*disk.Disk, interfaces []net.Interface) *api.HardwareInformation {
if s != nil {
return &api.HardwareInformation{
System: MapSystemInformation(s),
Compute: MapComputeInformation(s),
Memory: MapMemoryInformation(s),
Storage: MapStorageInformation(disks),
Network: MapNetworkInformation(interfaces),
}
}

return &api.HardwareInformation{
Storage: MapStorageInformation(disks),
}
}

func MapSystemInformation(s *smbios.SMBIOS) *api.SystemInformation {
return &api.SystemInformation{
Manufacturer: s.SystemInformation.Manufacturer,
ProductName: s.SystemInformation.ProductName,
SerialNumber: s.SystemInformation.SerialNumber,
Uuid: s.SystemInformation.UUID,
SkuNumber: s.SystemInformation.SKUNumber,
Family: s.SystemInformation.Family,
}
}

func MapComputeInformation(s *smbios.SMBIOS) *api.ComputeInformation {
var (
totalCoreCount = 0
totalThreadCount = 0
processors []*api.Processor
)

for _, v := range s.ProcessorInformation {
if v.Status.SocketPopulated() {
totalCoreCount += int(v.CoreCount)
totalThreadCount += int(v.ThreadCount)

processor := &api.Processor{
Manufacturer: v.ProcessorManufacturer,
ProductName: v.ProcessorVersion,
SerialNumber: v.SerialNumber,
Speed: uint32(v.CurrentSpeed),
CoreCount: uint32(v.CoreCount),
ThreadCount: uint32(v.ThreadCount),
}

processors = append(processors, processor)
}
}

return &api.ComputeInformation{
TotalCoreCount: uint32(totalCoreCount),
TotalThreadCount: uint32(totalThreadCount),
ProcessorCount: uint32(len(processors)),
Processors: processors,
}
}

func MapMemoryInformation(s *smbios.SMBIOS) *api.MemoryInformation {
var (
totalSize = 0
modules []*api.MemoryModule
)

for _, v := range s.MemoryDevices {
if v.Size != 0 && v.Size != 0xFFFF {
var size uint32

if v.Size == 0x7FFF {
totalSize += int(v.ExtendedSize)
size = uint32(v.ExtendedSize)
} else {
totalSize += v.Size.Megabytes()
size = uint32(v.Size)
}

memoryModule := &api.MemoryModule{
Manufacturer: v.Manufacturer,
ProductName: v.PartNumber,
SerialNumber: v.SerialNumber,
Type: v.MemoryType.String(),
Size: size,
Speed: uint32(v.Speed),
}

modules = append(modules, memoryModule)
}
}

return &api.MemoryInformation{
TotalSize: uint32(totalSize),
ModuleCount: uint32(len(modules)),
Modules: modules,
}
}

func MapStorageInformation(s []*disk.Disk) *api.StorageInformation {
totalSize := uint64(0)
devices := make([]*api.StorageDevice, 0, len(s))

for _, v := range s {
totalSize += v.Size

var storageType api.StorageType

switch v.Type.String() {
case "ssd":
storageType = api.StorageType_SSD
case "hdd":
storageType = api.StorageType_HDD
case "nvme":
storageType = api.StorageType_NVMe
case "sd":
storageType = api.StorageType_SD
default:
storageType = api.StorageType_Unknown
}

storageDevice := &api.StorageDevice{
Model: v.Model,
Serial: v.Serial,
Type: storageType,
Size: v.Size,
Name: v.Name,
DeviceName: v.DeviceName,
Uuid: v.UUID,
Wwid: v.WWID,
}

devices = append(devices, storageDevice)
}

return &api.StorageInformation{
TotalSize: totalSize,
DeviceCount: uint32(len(devices)),
Devices: devices,
}
}

func MapNetworkInformation(s []net.Interface) *api.NetworkInformation {
interfaces := make([]*api.NetworkInterface, 0, len(s))

for _, v := range s {
if len(v.HardwareAddr) == 0 {
continue // skip interfaces without a hardware address
}

addrs, err := v.Addrs()
if err != nil {
log.Printf("encountered error fetching addresses of network interface %q: %q", v.Name, err)

addrs = make([]net.Addr, 0)
}

var addresses []string

for _, a := range addrs {
addresses = append(addresses, a.String())
}

networkInterface := &api.NetworkInterface{
Index: uint32(v.Index),
Name: v.Name,
Flags: v.Flags.String(),
Mtu: uint32(v.MTU),
Mac: v.HardwareAddr.String(),
Addresses: addresses,
}

interfaces = append(interfaces, networkInterface)
}

return &api.NetworkInformation{
InterfaceCount: uint32(len(interfaces)),
Interfaces: interfaces,
}
}
15 changes: 15 additions & 0 deletions app/sidero-controller-manager/cmd/agent/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

//nolint:scopelint,testpackage
package main

import (
"testing"
)

//nolint:unparam
func TestMapHardwareInformation_DoesNotPanic(t *testing.T) {
MapHardwareInformation(nil, nil, nil)
}
Loading

0 comments on commit 7c7a377

Please sign in to comment.