初始提交:OCI 面板后端(含 GenAI 网关一期)
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
package oci
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/oracle/oci-go-sdk/v65/core"
|
||||
)
|
||||
|
||||
// Vnic 是实例已附加的虚拟网卡(附加关系与地址信息的合并视图)。
|
||||
type Vnic struct {
|
||||
AttachmentID string `json:"attachmentId"`
|
||||
VnicID string `json:"vnicId"`
|
||||
DisplayName string `json:"displayName"`
|
||||
IsPrimary bool `json:"isPrimary"`
|
||||
PrivateIP string `json:"privateIp"`
|
||||
PublicIP string `json:"publicIp"`
|
||||
Ipv6Addresses []string `json:"ipv6Addresses"`
|
||||
SubnetID string `json:"subnetId"`
|
||||
SubnetName string `json:"subnetName,omitempty"`
|
||||
LifecycleState string `json:"lifecycleState"`
|
||||
TimeCreated *time.Time `json:"timeCreated"`
|
||||
}
|
||||
|
||||
// AttachVnicInput 是附加新 VNIC 的输入;PrivateIP 留空自动分配。
|
||||
type AttachVnicInput struct {
|
||||
SubnetID string `json:"subnetId" binding:"required"`
|
||||
DisplayName string `json:"displayName"`
|
||||
PrivateIP string `json:"privateIp"`
|
||||
AssignPublicIP bool `json:"assignPublicIp"`
|
||||
// AssignIpv6 为 true 时同时自动分配一个 IPv6(要求子网已启用 IPv6)。
|
||||
AssignIpv6 bool `json:"assignIpv6"`
|
||||
}
|
||||
|
||||
// ListInstanceVnics 实现 Client:列出实例全部 VNIC(含附加中/分离中的关系)。
|
||||
func (c *RealClient) ListInstanceVnics(ctx context.Context, cred Credentials, region, instanceID string) ([]Vnic, error) {
|
||||
cc, err := c.computeClient(cred, region)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instResp, err := cc.GetInstance(ctx, core.GetInstanceRequest{InstanceId: &instanceID})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get instance %s: %w", instanceID, err)
|
||||
}
|
||||
// 附加关系在实例所在 compartment,传租户根会漏查
|
||||
attResp, err := cc.ListVnicAttachments(ctx, core.ListVnicAttachmentsRequest{
|
||||
CompartmentId: hostCompartment(cred, deref(instResp.CompartmentId)),
|
||||
InstanceId: &instanceID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list vnic attachments: %w", err)
|
||||
}
|
||||
vn, err := c.vcnClient(cred, region)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]Vnic, 0, len(attResp.Items))
|
||||
subnetNames := map[string]string{}
|
||||
for _, att := range attResp.Items {
|
||||
out = append(out, buildVnic(ctx, vn, att, subnetNames))
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// buildVnic 合并附加关系与 VNIC 详情;VNIC 未就绪(附加中)时只有关系字段。
|
||||
func buildVnic(ctx context.Context, vn core.VirtualNetworkClient, att core.VnicAttachment, subnetNames map[string]string) Vnic {
|
||||
v := Vnic{
|
||||
AttachmentID: deref(att.Id),
|
||||
VnicID: deref(att.VnicId),
|
||||
DisplayName: deref(att.DisplayName),
|
||||
SubnetID: deref(att.SubnetId),
|
||||
LifecycleState: string(att.LifecycleState),
|
||||
TimeCreated: sdkTime(att.TimeCreated),
|
||||
}
|
||||
if att.VnicId != nil {
|
||||
if resp, err := vn.GetVnic(ctx, core.GetVnicRequest{VnicId: att.VnicId}); err == nil {
|
||||
v.DisplayName = deref(resp.DisplayName)
|
||||
v.PrivateIP = deref(resp.PrivateIp)
|
||||
v.PublicIP = deref(resp.PublicIp)
|
||||
v.Ipv6Addresses = resp.Ipv6Addresses
|
||||
if resp.IsPrimary != nil {
|
||||
v.IsPrimary = *resp.IsPrimary
|
||||
}
|
||||
if deref(resp.SubnetId) != "" {
|
||||
v.SubnetID = deref(resp.SubnetId)
|
||||
}
|
||||
}
|
||||
}
|
||||
v.SubnetName = subnetNameOf(ctx, vn, v.SubnetID, subnetNames)
|
||||
return v
|
||||
}
|
||||
|
||||
// subnetNameOf 查子网显示名,map 去重;失败留空不影响列表。
|
||||
func subnetNameOf(ctx context.Context, vn core.VirtualNetworkClient, id string, cache map[string]string) string {
|
||||
if id == "" {
|
||||
return ""
|
||||
}
|
||||
if name, ok := cache[id]; ok {
|
||||
return name
|
||||
}
|
||||
name := ""
|
||||
if resp, err := vn.GetSubnet(ctx, core.GetSubnetRequest{SubnetId: &id}); err == nil {
|
||||
name = deref(resp.DisplayName)
|
||||
}
|
||||
cache[id] = name
|
||||
return name
|
||||
}
|
||||
|
||||
// AttachVnic 实现 Client:为实例附加次要 VNIC;实例须运行中且 shape 有空余 VNIC 配额。
|
||||
func (c *RealClient) AttachVnic(ctx context.Context, cred Credentials, region, instanceID string, in AttachVnicInput) (Vnic, error) {
|
||||
cc, err := c.computeClient(cred, region)
|
||||
if err != nil {
|
||||
return Vnic{}, err
|
||||
}
|
||||
details := &core.CreateVnicDetails{
|
||||
SubnetId: &in.SubnetID,
|
||||
AssignPublicIp: &in.AssignPublicIP,
|
||||
}
|
||||
if in.AssignIpv6 {
|
||||
details.AssignIpv6Ip = &in.AssignIpv6
|
||||
}
|
||||
if in.DisplayName != "" {
|
||||
details.DisplayName = &in.DisplayName
|
||||
}
|
||||
if in.PrivateIP != "" {
|
||||
details.PrivateIp = &in.PrivateIP
|
||||
}
|
||||
resp, err := cc.AttachVnic(ctx, core.AttachVnicRequest{
|
||||
AttachVnicDetails: core.AttachVnicDetails{InstanceId: &instanceID, CreateVnicDetails: details},
|
||||
})
|
||||
if err != nil {
|
||||
return Vnic{}, fmt.Errorf("attach vnic: %w", err)
|
||||
}
|
||||
att := resp.VnicAttachment
|
||||
return Vnic{
|
||||
AttachmentID: deref(att.Id),
|
||||
VnicID: deref(att.VnicId),
|
||||
DisplayName: deref(att.DisplayName),
|
||||
SubnetID: deref(att.SubnetId),
|
||||
LifecycleState: string(att.LifecycleState),
|
||||
TimeCreated: sdkTime(att.TimeCreated),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DetachVnic 实现 Client:分离次要 VNIC(主 VNIC 由 OCI 拒绝)。
|
||||
func (c *RealClient) DetachVnic(ctx context.Context, cred Credentials, region, attachmentID string) error {
|
||||
cc, err := c.computeClient(cred, region)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := cc.DetachVnic(ctx, core.DetachVnicRequest{VnicAttachmentId: &attachmentID}); err != nil {
|
||||
return fmt.Errorf("detach vnic %s: %w", attachmentID, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user