163 lines
5.4 KiB
Go
163 lines
5.4 KiB
Go
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"`
|
|
// ReservedPublicIPID 非空时不分配临时公网 IP,VNIC 就绪后由后台绑定该保留 IP。
|
|
ReservedPublicIPID string `json:"reservedPublicIpId"`
|
|
}
|
|
|
|
// 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
|
|
}
|
|
// 选了保留 IP 就不分配临时公网 IP,避免绑定时还要先删一次
|
|
if in.ReservedPublicIPID != "" {
|
|
in.AssignPublicIP = false
|
|
}
|
|
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
|
|
}
|