diff --git a/pkg/cloudprovider/cloud.go b/pkg/cloudprovider/cloud.go index 739a0c4e..68185410 100644 --- a/pkg/cloudprovider/cloud.go +++ b/pkg/cloudprovider/cloud.go @@ -22,6 +22,7 @@ type CloudInstance struct { Provider CloudProvider AcceleratorProtocol string Interfaces []NetworkInterface + Topology string } type NetworkInterface struct { diff --git a/pkg/cloudprovider/gce/gce.go b/pkg/cloudprovider/gce/gce.go index 1828cb8b..33f44136 100644 --- a/pkg/cloudprovider/gce/gce.go +++ b/pkg/cloudprovider/gce/gce.go @@ -20,6 +20,7 @@ import ( "context" "encoding/json" "fmt" + "strings" "time" "cloud.google.com/go/compute/metadata" @@ -94,6 +95,12 @@ func GetInstance(ctx context.Context) (*cloudprovider.CloudInstance, error) { klog.Infof("could not get network interfaces on GCE ... retrying: %v", err) return false, nil } + gceTopologyAttributes, err := metadata.GetWithContext(ctx, "instance/attributes/physical_host") + if err != nil { + klog.Infof("could not get physical host on GCE ... retrying: %v", err) + return false, nil + } + instance.Topology = gceTopologyAttributes return true, nil }) if err != nil { @@ -103,7 +110,7 @@ func GetInstance(ctx context.Context) (*cloudprovider.CloudInstance, error) { } // GetGCEAttributes fetches all attributes related to the provided GCP network. -func GetGCEAttributes(network string) map[resourceapi.QualifiedName]resourceapi.DeviceAttribute { +func GetGCEAttributes(network, topology string) map[resourceapi.QualifiedName]resourceapi.DeviceAttribute { attributes := make(map[resourceapi.QualifiedName]resourceapi.DeviceAttribute) var projectNumber int64 var name string @@ -114,7 +121,17 @@ func GetGCEAttributes(network string) map[resourceapi.QualifiedName]resourceapi. klog.Warningf("Error parsing network %q : %v", network, err) return nil } + topologyParts := strings.SplitN(strings.TrimPrefix(topology, "/"), "/", 3) + // topology may not be always available + if len(topologyParts) == 3 { + attributes["gce.dra.net/block"] = resourceapi.DeviceAttribute{StringValue: &topologyParts[0]} + attributes["gce.dra.net/subblock"] = resourceapi.DeviceAttribute{StringValue: &topologyParts[1]} + attributes["gce.dra.net/host"] = resourceapi.DeviceAttribute{StringValue: &topologyParts[2]} + } else { + klog.Warningf("Error parsing host topology, may be unsupported on VM %q : %v", topology, err) + } attributes["gce.dra.net/networkName"] = resourceapi.DeviceAttribute{StringValue: &name} attributes["gce.dra.net/networkProjectNumber"] = resourceapi.DeviceAttribute{IntValue: &projectNumber} + klog.Info(attributes) return attributes } diff --git a/pkg/inventory/cloud.go b/pkg/inventory/cloud.go index 4b3d74d7..eeae4034 100644 --- a/pkg/inventory/cloud.go +++ b/pkg/inventory/cloud.go @@ -59,7 +59,7 @@ func getProviderAttributes(mac string, instance *cloudprovider.CloudInstance) ma } for _, cloudInterface := range instance.Interfaces { if cloudInterface.Mac == mac { - return gce.GetGCEAttributes(cloudInterface.Network) + return gce.GetGCEAttributes(cloudInterface.Network, instance.Topology) } } klog.Warningf("no matching cloud interface found for mac %s", mac) diff --git a/pkg/inventory/cloud_test.go b/pkg/inventory/cloud_test.go index 84971f98..02e5f040 100644 --- a/pkg/inventory/cloud_test.go +++ b/pkg/inventory/cloud_test.go @@ -17,10 +17,10 @@ limitations under the License. package inventory import ( - "github.com/google/go-cmp/cmp" "testing" "github.com/google/dranet/pkg/cloudprovider" + "github.com/google/go-cmp/cmp" resourceapi "k8s.io/api/resource/v1beta1" "k8s.io/utils/ptr" ) @@ -67,10 +67,14 @@ func TestGetProviderAttributes(t *testing.T) { {Mac: "00:11:22:33:44:55", Network: "projects/12345/networks/test-network"}, {Mac: "AA:BB:CC:DD:EE:FF", Network: "projects/67890/networks/other-network"}, }, + Topology: "/block/subblock/host", }, want: map[resourceapi.QualifiedName]resourceapi.DeviceAttribute{ "gce.dra.net/networkName": {StringValue: ptr.To("test-network")}, "gce.dra.net/networkProjectNumber": {IntValue: ptr.To(int64(12345))}, + "gce.dra.net/block": {StringValue: ptr.To("block")}, + "gce.dra.net/subblock": {StringValue: ptr.To("subblock")}, + "gce.dra.net/host": {StringValue: ptr.To("host")}, }, }, { @@ -84,6 +88,22 @@ func TestGetProviderAttributes(t *testing.T) { }, want: nil, // gce.GetGCEAttributes returns nil for invalid network string }, + { + name: "GCE provider, MAC found, valid network, invalid topology", + mac: "00:11:22:33:44:55", + instance: &cloudprovider.CloudInstance{ + Provider: cloudprovider.CloudProviderGCE, + Interfaces: []cloudprovider.NetworkInterface{ + {Mac: "00:11:22:33:44:55", Network: "projects/12345/networks/test-network"}, + {Mac: "AA:BB:CC:DD:EE:FF", Network: "projects/67890/networks/other-network"}, + }, + Topology: "/block/subblock", + }, + want: map[resourceapi.QualifiedName]resourceapi.DeviceAttribute{ + "gce.dra.net/networkName": {StringValue: ptr.To("test-network")}, + "gce.dra.net/networkProjectNumber": {IntValue: ptr.To(int64(12345))}, + }, + }, { name: "Unsupported provider, MAC found", mac: "00:11:22:33:44:55",