Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend nic with mac #240

Merged
merged 11 commits into from
Nov 29, 2022
Merged
87 changes: 85 additions & 2 deletions nic.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,48 @@ type NICClient interface {
}

// OptionalNICParameters is an interface that declares the source of optional parameters for NIC creation.
type OptionalNICParameters interface{}
type OptionalNICParameters interface {
// represent mac_address for NIC
Mac() string
}

// BuildableNICParameters is a modifiable version of OptionalNICParameters. You can use CreateNICParams() to create a
// new copy, or implement your own.
type BuildableNICParameters interface {
OptionalNICParameters

// WithMac sets macAddress for the NIC.
WithMac(mac string) (BuildableNICParameters, error)

// MustWithMac is the same as WithMac, but panics instead of returning an error.
MustWithMac(mac string) BuildableNICParameters
}

// CreateNICParams returns a buildable structure of OptionalNICParameters.
func CreateNICParams() BuildableNICParameters {
return &nicParams{}
}

type nicParams struct{}
type nicParams struct {
mac string
}

func (c *nicParams) Mac() string {
return c.mac
}

func (c *nicParams) WithMac(mac string) (BuildableNICParameters, error) {
c.mac = mac
return c, nil
}

func (c *nicParams) MustWithMac(mac string) BuildableNICParameters {
builder, err := c.WithMac(mac)
if err != nil {
panic(err)
}
return builder
}

// UpdateNICParameters is an interface that declares methods of changeable parameters for NIC's. Each
// method can return nil to leave an attribute unchanged, or a new value for the attribute.
Expand All @@ -56,6 +84,9 @@ type UpdateNICParameters interface {

// VNICProfileID potentially returns a change VNIC profile for a NIC.
VNICProfileID() *VNICProfileID

// Mac potentially returns a change MacAddress for a nic
Mac() *string
}

// BuildableUpdateNICParameters is a buildable version of UpdateNICParameters.
Expand All @@ -71,6 +102,11 @@ type BuildableUpdateNICParameters interface {
WithVNICProfileID(id VNICProfileID) (BuildableUpdateNICParameters, error)
// MustWithVNICProfileID is identical to WithVNICProfileID, but panics instead of returning an error.
MustWithVNICProfileID(id VNICProfileID) BuildableUpdateNICParameters

// WithMac sets MaAddress of a NIC for the UpdateNIC method.
WithMac(mac string) (BuildableUpdateNICParameters, error)
// MustWithMac is identical to WithMac, but panics instead of returning an error.
MustWithMac(mac string) BuildableUpdateNICParameters
}

// UpdateNICParams creates a buildable UpdateNICParameters.
Expand All @@ -81,6 +117,7 @@ func UpdateNICParams() BuildableUpdateNICParameters {
type updateNICParams struct {
name *string
vnicProfileID *VNICProfileID
mac *string
}

func (u *updateNICParams) Name() *string {
Expand All @@ -91,6 +128,10 @@ func (u *updateNICParams) VNICProfileID() *VNICProfileID {
return u.vnicProfileID
}

func (u *updateNICParams) Mac() *string {
return u.mac
}

func (u *updateNICParams) WithName(name string) (BuildableUpdateNICParameters, error) {
u.name = &name
return u, nil
Expand All @@ -117,6 +158,19 @@ func (u *updateNICParams) MustWithVNICProfileID(id VNICProfileID) BuildableUpdat
return b
}

func (u *updateNICParams) WithMac(mac string) (BuildableUpdateNICParameters, error) {
u.mac = &mac
return u, nil
}

func (u *updateNICParams) MustWithMac(mac string) BuildableUpdateNICParameters {
b, err := u.WithMac(mac)
if err != nil {
panic(err)
}
return b
}

// NICData is the core of NIC which only provides data-access functions.
type NICData interface {
// ID is the identifier for this network interface.
Expand All @@ -127,6 +181,8 @@ type NICData interface {
VMID() VMID
// VNICProfileID returns the ID of the VNIC profile in use by the NIC.
VNICProfileID() VNICProfileID
// Mac returns a MacAddress for a nic
Mac() string
}

// NIC represents a network interface.
Expand Down Expand Up @@ -170,12 +226,21 @@ func convertSDKNIC(sdkObject *ovirtsdk.Nic, cli Client) (NIC, error) {
if !ok {
return nil, newFieldNotFound("vNIC Profile on VM", "ID")
}
mac, ok := sdkObject.Mac()
if !ok {
return nil, newFieldNotFound("mac", "NIC")
}
macAddr, ok := mac.Address()
if !ok {
return nil, newFieldNotFound("address", "mac")
}
return &nic{
cli,
NICID(id),
name,
VMID(vmid),
VNICProfileID(vnicProfileID),
macAddr,
}, nil
}

Expand All @@ -186,6 +251,7 @@ type nic struct {
name string
vmid VMID
vnicProfileID VNICProfileID
mac string
}

func (n nic) Update(params UpdateNICParameters, retries ...RetryStrategy) (NIC, error) {
Expand Down Expand Up @@ -216,6 +282,10 @@ func (n nic) VMID() VMID {
return n.vmid
}

func (n nic) Mac() string {
return n.mac
}

func (n nic) Remove(retries ...RetryStrategy) error {
return n.client.RemoveNIC(n.vmid, n.id, retries...)
}
Expand All @@ -227,6 +297,7 @@ func (n nic) withName(name string) *nic {
name: name,
vmid: n.vmid,
vnicProfileID: n.vnicProfileID,
mac: n.mac,
}
}

Expand All @@ -237,5 +308,17 @@ func (n nic) withVNICProfileID(vnicProfileID VNICProfileID) *nic {
name: n.name,
vmid: n.vmid,
vnicProfileID: vnicProfileID,
mac: n.mac,
}
}

func (n nic) withMac(mac string) *nic {
return &nic{
client: n.client,
id: n.id,
name: n.name,
vmid: n.vmid,
vnicProfileID: n.vnicProfileID,
mac: mac,
}
}
34 changes: 32 additions & 2 deletions nic_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package ovirtclient

import (
"fmt"
"net"

"github.com/google/uuid"
ovirtsdk "github.com/ovirt/go-ovirt"
Expand All @@ -11,13 +12,21 @@ func (o *oVirtClient) CreateNIC(
vmid VMID,
vnicProfileID VNICProfileID,
name string,
_ OptionalNICParameters,
params OptionalNICParameters,
retries ...RetryStrategy,
) (result NIC, err error) {
if err := validateNICCreationParameters(vmid, name); err != nil {
return nil, err
}

var mac string
if params != nil {
if err := validateNICCreationOptionalParameters(params); err != nil {
return nil, err
}
mac = params.Mac()
}

retries = defaultRetries(retries, defaultReadTimeouts(o))
err = retry(
fmt.Sprintf("creating NIC for VM %s", vmid),
Expand All @@ -27,6 +36,11 @@ func (o *oVirtClient) CreateNIC(
nicBuilder := ovirtsdk.NewNicBuilder()
nicBuilder.Name(name)
nicBuilder.VnicProfile(ovirtsdk.NewVnicProfileBuilder().Id(string(vnicProfileID)).MustBuild())

if mac != "" {
nicBuilder.Mac(ovirtsdk.NewMacBuilder().Address(mac).MustBuild())
}

nic := nicBuilder.MustBuild()

response, err := o.conn.SystemService().VmsService().VmService(string(vmid)).NicsService().Add().Nic(nic).Send()
Expand Down Expand Up @@ -60,7 +74,7 @@ func (m *mockClient) CreateNIC(
vmid VMID,
vnicProfileID VNICProfileID,
name string,
_ OptionalNICParameters,
params OptionalNICParameters,
_ ...RetryStrategy,
) (NIC, error) {
m.lock.Lock()
Expand All @@ -87,6 +101,13 @@ func (m *mockClient) CreateNIC(
vmid: vmid,
vnicProfileID: vnicProfileID,
}

if params != nil {
if mac := params.Mac(); mac != "" {
nic.mac = mac
}
}

m.nics[id] = nic

return nic, nil
Expand All @@ -101,3 +122,12 @@ func validateNICCreationParameters(vmid VMID, name string) error {
}
return nil
}

func validateNICCreationOptionalParameters(params OptionalNICParameters) error {
if mac := params.Mac(); mac != "" {
if _, err := net.ParseMAC(mac); err != nil {
return newError(EUnidentified, "Failed to parse MacAddress: %s", mac)
}
}
return nil
}
19 changes: 19 additions & 0 deletions nic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,22 @@ func assertCanUpdateNICVNICProfile(t *testing.T, nic ovirtclient.NIC, vnicProfil
}
return newNIC
}

func assertCanUpdateNICMac(t *testing.T, nic ovirtclient.NIC, mac string) ovirtclient.NIC {
newNIC, err := nic.Update(ovirtclient.UpdateNICParams().MustWithMac(mac))
if err != nil {
t.Fatalf("failed to update NIC (%v)", err)
}
if newNIC.Mac() != mac {
t.Fatalf("NIC MacAddress not changed after update call")
}
return newNIC
}

func assertCantUpdateNICMac(t *testing.T, nic ovirtclient.NIC, mac string) ovirtclient.NIC {
newNIC, err := nic.Update(ovirtclient.UpdateNICParams().MustWithMac(mac))
if err == nil {
t.Fatalf("Mac address validation error. Invalid mac was accepted.")
}
return newNIC
}
13 changes: 13 additions & 0 deletions nic_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package ovirtclient

import (
"fmt"
"net"

ovirtsdk "github.com/ovirt/go-ovirt"
)
Expand All @@ -21,6 +22,12 @@ func (o *oVirtClient) UpdateNIC(
if vnicProfileID := params.VNICProfileID(); vnicProfileID != nil {
nicBuilder.VnicProfile(ovirtsdk.NewVnicProfileBuilder().Id(string(*vnicProfileID)).MustBuild())
}
if mac := params.Mac(); mac != nil {
if _, err := net.ParseMAC(*mac); err != nil {
return nil, newError(EUnidentified, "Failed to parse MacAddress: %s", *mac)
}
nicBuilder.Mac(ovirtsdk.NewMacBuilder().Address(*mac).MustBuild())
}

req.Nic(nicBuilder.MustBuild())

Expand Down Expand Up @@ -70,6 +77,12 @@ func (m *mockClient) UpdateNIC(vmid VMID, nicID NICID, params UpdateNICParameter
}
nic = nic.withVNICProfileID(*vnicProfileID)
}
if mac := params.Mac(); mac != nil {
if _, err := net.ParseMAC(*mac); err != nil {
return nil, newError(EUnidentified, "Failed to parse MacAddress: %s", *mac)
}
nic = nic.withMac(*mac)
}
m.nics[nicID] = nic

return nic, nil
Expand Down
2 changes: 2 additions & 0 deletions nic_update_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ func TestVMNICUpdate(t *testing.T) {
nic = assertCanUpdateNICName(t, nic, fmt.Sprintf("test-%s", helper.GenerateRandomID(5)))
vnicProfile := assertCanCreateVNICProfile(t, helper)
nic = assertCanUpdateNICVNICProfile(t, nic, vnicProfile.ID())
nic = assertCanUpdateNICMac(t, nic, "a1:b2:c3:d4:e5:f6")
_ = assertCantUpdateNICMac(t, nic, "invalid mac address")
// Go back to the original VNIC profile ID to make sure we don't block deleting the test VNIC profile.
_ = assertCanUpdateNICVNICProfile(t, nic, helper.GetVNICProfileID())
}