Skip to content

Commit

Permalink
Add dhcp network support
Browse files Browse the repository at this point in the history
  • Loading branch information
Karthik-K-N committed Apr 24, 2024
1 parent d9e5cb3 commit 13bf055
Show file tree
Hide file tree
Showing 8 changed files with 258 additions and 234 deletions.
11 changes: 10 additions & 1 deletion builder/powervs/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package powervs

import (
"context"

"github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer-plugin-sdk/common"
"github.com/hashicorp/packer-plugin-sdk/communicator"
Expand Down Expand Up @@ -84,13 +85,20 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
return nil, err
}

dhcpClient, err := b.config.DHCPClient(ctx, b.config.ServiceInstanceID)
if err != nil {
return nil, err
}

var steps []multistep.Step

steps = append(steps,
&StepImageBaseImage{
Source: b.config.Source,
},
&StepCreateNetwork{},
&StepCreateNetwork{
DHCPNetwork: b.config.DHCPNetwork,
},
&StepCreateInstance{
InstanceName: b.config.InstanceName,
KeyPairName: b.config.KeyPairName,
Expand All @@ -117,6 +125,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
state.Put("jobClient", jobClient)
state.Put("instanceClient", instanceClient)
state.Put("networkClient", networkClient)
state.Put("dhcpClient", dhcpClient)

// Run!
b.runner = commonsteps.NewRunner(steps, b.config.PackerConfig, ui)
Expand Down
2 changes: 2 additions & 0 deletions builder/powervs/builder.hcl2spec.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions builder/powervs/common/access_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,11 @@ func (c *AccessConfig) JobClient(ctx context.Context, id string) (*instance.IBMP
}
return instance.NewIBMPIJobClient(ctx, session, id), nil
}

func (c *AccessConfig) DHCPClient(ctx context.Context, id string) (*instance.IBMPIDhcpClient, error) {
session, err := c.Session()
if err != nil {
return nil, err
}
return instance.NewIBMPIDhcpClient(ctx, session, id), nil
}
1 change: 1 addition & 0 deletions builder/powervs/common/run_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type CaptureCOS struct {
type RunConfig struct {
InstanceName string `mapstructure:"instance_name" required:"true"`
KeyPairName string `mapstructure:"key_pair_name" required:"true"`
DHCPNetwork bool `mapstructure:"dhcp_network" required:"false"`
Source Source `mapstructure:"source" required:"true"`
Capture Capture `mapstructure:"capture" required:"true"`

Expand Down
62 changes: 59 additions & 3 deletions builder/powervs/common/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ package common

import (
"errors"
"fmt"
"time"

"github.com/IBM-Cloud/power-go-client/clients/instance"
"github.com/IBM-Cloud/power-go-client/power/models"
"github.com/hashicorp/packer-plugin-sdk/multistep"
"time"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)

var (
Expand All @@ -17,9 +20,11 @@ var (
// for determining the SSH address of the instance.
func SSHHost() func(multistep.StateBag) (string, error) {
return func(state multistep.StateBag) (string, error) {
ui := state.Get("ui").(packersdk.Ui)
ui.Message("Fetching IP for machine")
instanceClient := state.Get("instanceClient").(*instance.IBMPIInstanceClient)
host := ""
const tries = 15
const tries = 25
for j := 0; j <= tries; j++ {
i := state.Get("instance").(*models.PVMInstance)
in, err := instanceClient.Get(*i.PvmInstanceID)
Expand All @@ -34,9 +39,60 @@ func SSHHost() func(multistep.StateBag) (string, error) {
if host != "" {
return host, nil
}

dhcpServerID, ok := state.GetOk("dhcpServerID")
if !ok {
// if the dhcpServerID is not set, dont try to fetch IP from DHCP server, instead wait for address to get populated.
ui.Message("Machine IP is not yet found, Trying again")
time.Sleep(sshHostSleepDuration)
continue
}
dhcpClient := state.Get("dhcpClient").(*instance.IBMPIDhcpClient)
ui.Message("Getting Instance IP from DHCP server")

net := state.Get("network").(*models.Network)
networkID := net.NetworkID

var pvmNetwork *models.PVMInstanceNetwork
for _, network := range in.Networks {
if network.NetworkID == *networkID {
pvmNetwork = network
ui.Message("Found network attached to VM")
}
}

if pvmNetwork == nil {
ui.Message("Failed to get network attached to VM, Trying again")
time.Sleep(sshHostSleepDuration)
continue
}

dhcpServerDetails, err := dhcpClient.Get(dhcpServerID.(string))
if err != nil {
ui.Error(fmt.Sprintf("Failed to get DHCP server details: %v", err))
return "", err
}

if dhcpServerDetails == nil {
ui.Error(fmt.Sprintf("DHCP server details is nil, DHCPServerID: %s", dhcpServerID))
return "", err
}

var internalIP string
for _, lease := range dhcpServerDetails.Leases {
if *lease.InstanceMacAddress == pvmNetwork.MacAddress {
ui.Message(fmt.Sprintf("Found internal ip for VM from DHCP lease IP %s", *lease.InstanceIP))
internalIP = *lease.InstanceIP
break
}
}
if internalIP != "" {
return internalIP, nil
}

ui.Message("Machine IP is not yet found from DHCP server lease, Trying again")
time.Sleep(sshHostSleepDuration)
}

return "", errors.New("couldn't determine address for instance")
}
}
Expand Down
82 changes: 79 additions & 3 deletions builder/powervs/step_create_network.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,48 @@ package powervs
import (
"context"
"fmt"
"time"

"github.com/IBM-Cloud/power-go-client/clients/instance"
"github.com/IBM-Cloud/power-go-client/power/models"
"github.com/IBM/go-sdk-core/v5/core"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)

const (
// DHCPServerActiveTimeOut is time to wait for DHCP Server status to become active.
DHCPServerActiveTimeOut = 15 * time.Minute
// DHCPServerInterval is time to sleep before checking DHCP Server status.
DHCPServerInterval = 1 * time.Minute
)

type StepCreateNetwork struct {
doCleanup bool
DHCPNetwork bool
doCleanup bool
}

func (s *StepCreateNetwork) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
ui.Say("Creating Instance")

networkClient := state.Get("networkClient").(*instance.IBMPINetworkClient)

// If CreateDHCPNetwork is set, Create DHCP network.
if s.DHCPNetwork {
ui.Say("Creating DHCP network")
if err := s.createDHCPNetwork(state); err != nil {
ui.Error(fmt.Sprintf("failed to create DHCP network: %v", err))
return multistep.ActionHalt
}
s.doCleanup = true
return multistep.ActionContinue
}

ui.Say("Creating network")
netBody := &models.NetworkCreate{
DNSServers: []string{"8.8.8.8", "9.9.9.9"},
Type: core.StringPtr("pub-vlan"),
}
ui.Message("Creating Network")
net, err := networkClient.Create(netBody)
if err != nil {
ui.Error(fmt.Sprintf("failed to create network: %v", err))
Expand All @@ -46,11 +66,67 @@ func (s *StepCreateNetwork) Cleanup(state multistep.StateBag) {
ui := state.Get("ui").(packersdk.Ui)

ui.Say("Deleting the Network")

if s.DHCPNetwork {
ui.Message("Deleting DHCP server")
dhcpServerID := state.Get("dhcpServerID").(string)
dhcpClient := state.Get("dhcpClient").(*instance.IBMPIDhcpClient)

if err := dhcpClient.Delete(dhcpServerID); err != nil {
ui.Error(fmt.Sprintf("Error cleaning up DHCP server. Please delete the DHCP server manually: %s error: %v", dhcpServerID, err.Error()))
}
ui.Message("Successfully deleted DHCP server")
return
}
networkClient := state.Get("networkClient").(*instance.IBMPINetworkClient)
net := state.Get("network").(*models.Network)
err := networkClient.Delete(*net.NetworkID)
if err != nil {
ui.Error(fmt.Sprintf(
"Error cleaning up network. Please delete the network manually: %s", *net.Name))
}
ui.Message("Successfully deleted network")
}

func (s *StepCreateNetwork) createDHCPNetwork(state multistep.StateBag) error {
ui := state.Get("ui").(packersdk.Ui)
dhcpClient := state.Get("dhcpClient").(*instance.IBMPIDhcpClient)

dhcpServer, err := dhcpClient.Create(&models.DHCPServerCreate{})
if err != nil {
return fmt.Errorf("error failed to create DHCP server: %v", err)
}

if dhcpServer.ID == nil {
return fmt.Errorf("error created DHCP server ID is nil")
}
state.Put("dhcpServerID", *dhcpServer.ID)

startTime := time.Now()
var networkID string
for {
dhcpServerDetails, err := dhcpClient.Get(*dhcpServer.ID)
if err != nil {
return err
}
if dhcpServerDetails.Network != nil && dhcpServerDetails.Network.ID != nil {
networkID = *dhcpServerDetails.Network.ID
ui.Message("DHCP server in active state")
break
}
if time.Since(startTime) > DHCPServerActiveTimeOut {
return fmt.Errorf("error DHCP server did not become active even after %f min", DHCPServerActiveTimeOut.Minutes())
}
ui.Message("Wating for DHCP server to become active")
time.Sleep(DHCPServerInterval)
}
ui.Say("Fetching network details")
networkClient := state.Get("networkClient").(*instance.IBMPINetworkClient)
// fetch the dhcp network details and store it for future usage.
net, err := networkClient.Get(networkID)
if err != nil {
return fmt.Errorf("error fetching network details with network id %s error: %v", networkID, err)
}
state.Put("network", net)
return nil
}
Loading

0 comments on commit 13bf055

Please sign in to comment.