Files

130 lines
4.4 KiB
Go

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
}