package oci import ( "context" "fmt" "time" "github.com/oracle/oci-go-sdk/v65/common" "github.com/oracle/oci-go-sdk/v65/monitoring" ) // trafficNamespace 是 VNIC 流量指标所在的 Monitoring 命名空间; // 指标由 OCI 自动上报,原始点 1 分钟一个,这里按天聚合。 const ( trafficNamespace = "oci_vcn" trafficResolution = "1440m" metricVnicInbound = "VnicFromNetworkBytes" // 实例收到的字节 metricVnicOutbund = "VnicToNetworkBytes" // 实例发出的字节 ) // TrafficQuery 是一次实例流量统计的条件。 type TrafficQuery struct { Region string InstanceID string StartTime time.Time EndTime time.Time } // TrafficPoint 是一个聚合窗口(天)的流量字节数。 type TrafficPoint struct { Timestamp time.Time `json:"timestamp"` Bytes float64 `json:"bytes"` } // VnicTraffic 是单块 VNIC 的进出流量。 type VnicTraffic struct { VnicID string `json:"vnicId"` InboundBytes float64 `json:"inboundBytes"` OutboundBytes float64 `json:"outboundBytes"` Inbound []TrafficPoint `json:"inbound"` Outbound []TrafficPoint `json:"outbound"` } // InstanceTraffic 是实例全部 VNIC 的流量汇总。 type InstanceTraffic struct { InstanceID string `json:"instanceId"` InboundBytes float64 `json:"inboundBytes"` OutboundBytes float64 `json:"outboundBytes"` Vnics []VnicTraffic `json:"vnics"` } func (c *RealClient) monitoringClient(cred Credentials, region string) (monitoring.MonitoringClient, error) { mc, err := monitoring.NewMonitoringClientWithConfigurationProvider(provider(cred)) if err != nil { return mc, fmt.Errorf("new monitoring client: %w", err) } applyProxy(&mc.BaseClient, cred) if region != "" { mc.SetRegion(normalizeRegion(region)) } return mc, nil } // SummarizeInstanceTraffic 实现 Client:逐 VNIC 查询进出流量并汇总。 func (c *RealClient) SummarizeInstanceTraffic(ctx context.Context, cred Credentials, q TrafficQuery) (InstanceTraffic, error) { mc, err := c.monitoringClient(cred, q.Region) if err != nil { return InstanceTraffic{}, err } // VNIC 指标上报在实例所在 compartment,查询须用同一 compartment 才有数据 vnics, compartmentID, err := c.instanceVnics(ctx, cred, q.Region, q.InstanceID) if err != nil { return InstanceTraffic{}, err } result := InstanceTraffic{InstanceID: q.InstanceID, Vnics: make([]VnicTraffic, 0, len(vnics))} for _, vnic := range vnics { vt := VnicTraffic{VnicID: deref(vnic.Id)} if vt.Inbound, err = summarizeVnicMetric(ctx, mc, compartmentID, vt.VnicID, metricVnicInbound, q); err != nil { return InstanceTraffic{}, err } if vt.Outbound, err = summarizeVnicMetric(ctx, mc, compartmentID, vt.VnicID, metricVnicOutbund, q); err != nil { return InstanceTraffic{}, err } vt.Inbound, vt.Outbound = orEmpty(vt.Inbound), orEmpty(vt.Outbound) vt.InboundBytes, vt.OutboundBytes = sumTraffic(vt.Inbound), sumTraffic(vt.Outbound) result.InboundBytes += vt.InboundBytes result.OutboundBytes += vt.OutboundBytes result.Vnics = append(result.Vnics, vt) } return result, nil } // summarizeVnicMetric 按天聚合查询单块 VNIC 的一个流量指标; // compartmentID 须是实例所在 compartment,指标数据挂在该 compartment 下。 func summarizeVnicMetric(ctx context.Context, mc monitoring.MonitoringClient, compartmentID, vnicID, metric string, q TrafficQuery) ([]TrafficPoint, error) { query := fmt.Sprintf("%s[%s]{resourceId = %q}.sum()", metric, trafficResolution, vnicID) resolution := trafficResolution resp, err := mc.SummarizeMetricsData(ctx, monitoring.SummarizeMetricsDataRequest{ CompartmentId: &compartmentID, SummarizeMetricsDataDetails: monitoring.SummarizeMetricsDataDetails{ Namespace: common.String(trafficNamespace), Query: &query, StartTime: &common.SDKTime{Time: q.StartTime}, EndTime: &common.SDKTime{Time: q.EndTime}, Resolution: &resolution, }, }) if err != nil { return nil, fmt.Errorf("summarize %s of vnic %s: %w", metric, vnicID, err) } var points []TrafficPoint for _, item := range resp.Items { for _, dp := range item.AggregatedDatapoints { if dp.Timestamp == nil || dp.Value == nil { continue } points = append(points, TrafficPoint{Timestamp: dp.Timestamp.Time, Bytes: *dp.Value}) } } return orEmpty(points), nil } func sumTraffic(points []TrafficPoint) float64 { var total float64 for _, p := range points { total += p.Bytes } return total }