@@ -0,0 +1,197 @@
|
||||
package oci
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/oracle/oci-go-sdk/v65/common"
|
||||
"github.com/oracle/oci-go-sdk/v65/core"
|
||||
)
|
||||
|
||||
// ReservedIP 是保留公网 IP 摘要;绑定目标尽力反查,失败时对应字段为空。
|
||||
type ReservedIP struct {
|
||||
ID string `json:"id"`
|
||||
DisplayName string `json:"displayName"`
|
||||
IPAddress string `json:"ipAddress"`
|
||||
LifecycleState string `json:"lifecycleState"`
|
||||
AssignedInstanceID string `json:"assignedInstanceId"`
|
||||
AssignedInstanceName string `json:"assignedInstanceName"`
|
||||
TimeCreated *time.Time `json:"timeCreated"`
|
||||
}
|
||||
|
||||
// ListReservedIPs 实现 Client:列出区间内全部保留公网 IP(REGION 作用域);
|
||||
// compartmentID 为空时用生效 compartment。
|
||||
func (c *RealClient) ListReservedIPs(ctx context.Context, cred Credentials, region, compartmentID string) ([]ReservedIP, error) {
|
||||
vn, err := c.vcnClient(cred, region)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc, err := c.computeClient(cred, region)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]ReservedIP, 0, 4)
|
||||
var page *string
|
||||
for {
|
||||
resp, err := vn.ListPublicIps(ctx, core.ListPublicIpsRequest{
|
||||
Scope: core.ListPublicIpsScopeRegion,
|
||||
Lifetime: core.ListPublicIpsLifetimeReserved,
|
||||
CompartmentId: hostCompartment(cred, compartmentID),
|
||||
Page: page,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list reserved ips: %w", err)
|
||||
}
|
||||
for i := range resp.Items {
|
||||
out = append(out, c.toReservedIP(ctx, vn, cc, resp.Items[i]))
|
||||
}
|
||||
if resp.OpcNextPage == nil {
|
||||
break
|
||||
}
|
||||
page = resp.OpcNextPage
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// toReservedIP 转换 SDK 对象并尽力反查绑定的实例。
|
||||
func (c *RealClient) toReservedIP(ctx context.Context, vn core.VirtualNetworkClient, cc core.ComputeClient, p core.PublicIp) ReservedIP {
|
||||
var created *time.Time
|
||||
if p.TimeCreated != nil {
|
||||
created = &p.TimeCreated.Time
|
||||
}
|
||||
r := ReservedIP{
|
||||
ID: deref(p.Id),
|
||||
DisplayName: deref(p.DisplayName),
|
||||
IPAddress: deref(p.IpAddress),
|
||||
LifecycleState: string(p.LifecycleState),
|
||||
TimeCreated: created,
|
||||
}
|
||||
if p.AssignedEntityType == core.PublicIpAssignedEntityTypePrivateIp && p.AssignedEntityId != nil {
|
||||
r.AssignedInstanceID, r.AssignedInstanceName = resolveIPAssignee(ctx, vn, cc, *p.AssignedEntityId)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// resolveIPAssignee 由私有 IP 反查所属实例;任一步失败即放弃,不影响列表主体。
|
||||
func resolveIPAssignee(ctx context.Context, vn core.VirtualNetworkClient, cc core.ComputeClient, privateIPID string) (string, string) {
|
||||
priv, err := vn.GetPrivateIp(ctx, core.GetPrivateIpRequest{PrivateIpId: &privateIPID})
|
||||
if err != nil || priv.VnicId == nil {
|
||||
return "", ""
|
||||
}
|
||||
atts, err := cc.ListVnicAttachments(ctx, core.ListVnicAttachmentsRequest{
|
||||
CompartmentId: priv.CompartmentId,
|
||||
VnicId: priv.VnicId,
|
||||
})
|
||||
if err != nil || len(atts.Items) == 0 || atts.Items[0].InstanceId == nil {
|
||||
return "", ""
|
||||
}
|
||||
instanceID := *atts.Items[0].InstanceId
|
||||
inst, err := cc.GetInstance(ctx, core.GetInstanceRequest{InstanceId: &instanceID})
|
||||
if err != nil {
|
||||
return instanceID, ""
|
||||
}
|
||||
return instanceID, deref(inst.DisplayName)
|
||||
}
|
||||
|
||||
// CreateReservedIP 实现 Client:在指定 compartment 创建保留公网 IP(空为生效 compartment)。
|
||||
func (c *RealClient) CreateReservedIP(ctx context.Context, cred Credentials, region, compartmentID, displayName string) (ReservedIP, error) {
|
||||
vn, err := c.vcnClient(cred, region)
|
||||
if err != nil {
|
||||
return ReservedIP{}, err
|
||||
}
|
||||
details := core.CreatePublicIpDetails{
|
||||
CompartmentId: hostCompartment(cred, compartmentID),
|
||||
Lifetime: core.CreatePublicIpDetailsLifetimeReserved,
|
||||
}
|
||||
if displayName != "" {
|
||||
details.DisplayName = &displayName
|
||||
}
|
||||
resp, err := vn.CreatePublicIp(ctx, core.CreatePublicIpRequest{CreatePublicIpDetails: details})
|
||||
if err != nil {
|
||||
return ReservedIP{}, fmt.Errorf("create reserved ip: %w", err)
|
||||
}
|
||||
cc, err := c.computeClient(cred, region)
|
||||
if err != nil {
|
||||
return ReservedIP{}, err
|
||||
}
|
||||
return c.toReservedIP(ctx, vn, cc, resp.PublicIp), nil
|
||||
}
|
||||
|
||||
// AssignReservedIP 实现 Client:绑定保留 IP 到实例主私有 IP;instanceID 为空表示解绑。
|
||||
// 目标私有 IP 已有公网 IP 时自动释放(临时删除、其他保留解绑),地址会变更,调用方需提示用户。
|
||||
func (c *RealClient) AssignReservedIP(ctx context.Context, cred Credentials, region, publicIPID, instanceID string) error {
|
||||
vn, err := c.vcnClient(cred, region)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
target := common.String("")
|
||||
if instanceID != "" {
|
||||
priv, err := c.primaryPrivateIP(ctx, cred, region, instanceID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := detachExistingPublicIP(ctx, vn, priv.Id); err != nil {
|
||||
return err
|
||||
}
|
||||
target = priv.Id
|
||||
}
|
||||
_, err = vn.UpdatePublicIp(ctx, core.UpdatePublicIpRequest{
|
||||
PublicIpId: &publicIPID,
|
||||
UpdatePublicIpDetails: core.UpdatePublicIpDetails{PrivateIpId: target},
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("assign reserved ip: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AssignReservedIPToVnic 实现 Client:绑定保留 IP 到指定 VNIC 的主私有 IP(次要网卡场景);
|
||||
// 该私有 IP 已有公网 IP 时自动释放(临时删除、其他保留解绑)。
|
||||
func (c *RealClient) AssignReservedIPToVnic(ctx context.Context, cred Credentials, region, publicIPID, vnicID string) error {
|
||||
vn, err := c.vcnClient(cred, region)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
priv, err := vnicPrimaryPrivateIP(ctx, vn, vnicID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := detachExistingPublicIP(ctx, vn, priv.Id); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = vn.UpdatePublicIp(ctx, core.UpdatePublicIpRequest{
|
||||
PublicIpId: &publicIPID,
|
||||
UpdatePublicIpDetails: core.UpdatePublicIpDetails{PrivateIpId: priv.Id},
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("assign reserved ip to vnic: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// vnicPrimaryPrivateIP 取 VNIC 的主私有 IP。
|
||||
func vnicPrimaryPrivateIP(ctx context.Context, vn core.VirtualNetworkClient, vnicID string) (core.PrivateIp, error) {
|
||||
resp, err := vn.ListPrivateIps(ctx, core.ListPrivateIpsRequest{VnicId: &vnicID})
|
||||
if err != nil {
|
||||
return core.PrivateIp{}, fmt.Errorf("list private ips: %w", err)
|
||||
}
|
||||
for _, p := range resp.Items {
|
||||
if p.IsPrimary != nil && *p.IsPrimary {
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
return core.PrivateIp{}, fmt.Errorf("assign reserved ip: vnic has no primary private ip")
|
||||
}
|
||||
|
||||
// DeleteReservedIP 实现 Client:删除保留公网 IP(已绑定的会先被 OCI 拒绝)。
|
||||
func (c *RealClient) DeleteReservedIP(ctx context.Context, cred Credentials, region, publicIPID string) error {
|
||||
vn, err := c.vcnClient(cred, region)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := vn.DeletePublicIp(ctx, core.DeletePublicIpRequest{PublicIpId: &publicIPID}); err != nil {
|
||||
return fmt.Errorf("delete reserved ip: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user