From 5fe9f3980d597d9a8b241328a321545c0c148b02 Mon Sep 17 00:00:00 2001 From: John-Paul Robinson Date: Wed, 17 Jul 2019 17:00:28 -0500 Subject: [PATCH 1/3] Fix #7171: select instance network on which to assign floating ip Add config parameter instance_floating_ip_net to control floating ip assignment for a multi-homed instances. This ensures the floating ip can be correctly assigned to the instance port that is connected to the preferred network for floating ip assignment. This avoids the default first-returned selection method which may choose a network to which floating ip's can't be assigned, e.g. because that network is not reachable from the floating ip network. --- builder/openstack/builder.go | 1 + builder/openstack/networks.go | 18 ++++++++++++++++-- builder/openstack/run_config.go | 1 + builder/openstack/step_allocate_ip.go | 3 ++- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/builder/openstack/builder.go b/builder/openstack/builder.go index b69181daf..7ff34b869 100644 --- a/builder/openstack/builder.go +++ b/builder/openstack/builder.go @@ -129,6 +129,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack FloatingIPNetwork: b.config.FloatingIPNetwork, FloatingIP: b.config.FloatingIP, ReuseIPs: b.config.ReuseIPs, + InstanceFloatingIPNet: b.config.InstanceFloatingIPNet, }, &communicator.StepConnect{ Config: &b.config.RunConfig.Comm, diff --git a/builder/openstack/networks.go b/builder/openstack/networks.go index 30c435ee8..0635abc86 100644 --- a/builder/openstack/networks.go +++ b/builder/openstack/networks.go @@ -2,6 +2,7 @@ package openstack import ( "fmt" + "log" "github.com/google/uuid" "github.com/gophercloud/gophercloud" @@ -66,7 +67,10 @@ func FindFreeFloatingIP(client *gophercloud.ServiceClient) (*floatingips.Floatin // GetInstancePortID returns internal port of the instance that can be used for // the association of a floating IP. // It will return an ID of a first port if there are many. -func GetInstancePortID(client *gophercloud.ServiceClient, id string) (string, error) { +func GetInstancePortID(client *gophercloud.ServiceClient, id string, instance_float_net string) (string, error) { + + selected_interface := 0 + interfacesPage, err := attachinterfaces.List(client, id).AllPages() if err != nil { return "", err @@ -79,7 +83,17 @@ func GetInstancePortID(client *gophercloud.ServiceClient, id string) (string, er return "", fmt.Errorf("instance '%s' has no interfaces", id) } - return interfaces[0].PortID, nil + for i := 0; i < len(interfaces); i++ { + log.Printf("Instance interface: %v: %+v\n", i, interfaces[i]) + if interfaces[i].NetID == instance_float_net { + log.Printf("Found preferred interface: %v\n", i) + selected_interface = i + log.Printf("Using interface value: %v", selected_interface) + } + } + + + return interfaces[selected_interface].PortID, nil } // CheckFloatingIPNetwork checks provided network reference and returns a valid diff --git a/builder/openstack/run_config.go b/builder/openstack/run_config.go index c0bac7e6f..a41382edb 100644 --- a/builder/openstack/run_config.go +++ b/builder/openstack/run_config.go @@ -28,6 +28,7 @@ type RunConfig struct { ReuseIPs bool `mapstructure:"reuse_ips"` SecurityGroups []string `mapstructure:"security_groups"` Networks []string `mapstructure:"networks"` + InstanceFloatingIPNet string `mapstructure:"instance_floating_ip_net"` Ports []string `mapstructure:"ports"` UserData string `mapstructure:"user_data"` UserDataFile string `mapstructure:"user_data_file"` diff --git a/builder/openstack/step_allocate_ip.go b/builder/openstack/step_allocate_ip.go index a63faa836..2043cb75c 100644 --- a/builder/openstack/step_allocate_ip.go +++ b/builder/openstack/step_allocate_ip.go @@ -14,6 +14,7 @@ type StepAllocateIp struct { FloatingIPNetwork string FloatingIP string ReuseIPs bool + InstanceFloatingIPNet string } func (s *StepAllocateIp) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { @@ -114,7 +115,7 @@ func (s *StepAllocateIp) Run(ctx context.Context, state multistep.StateBag) mult ui.Say(fmt.Sprintf("Associating floating IP '%s' (%s) with instance port...", instanceIP.ID, instanceIP.FloatingIP)) - portID, err := GetInstancePortID(computeClient, server.ID) + portID, err := GetInstancePortID(computeClient, server.ID, s.InstanceFloatingIPNet) if err != nil { err := fmt.Errorf("Error getting interfaces of the instance '%s': %s", server.ID, err) state.Put("error", err) From 038e9f316ca9de9b19d593a013c44cd57933618b Mon Sep 17 00:00:00 2001 From: John-Paul Robinson Date: Wed, 17 Jul 2019 17:10:52 -0500 Subject: [PATCH 2/3] Clean patch formatting with make fmt --- builder/openstack/builder.go | 8 +++--- builder/openstack/networks.go | 7 +++--- builder/openstack/run_config.go | 36 +++++++++++++-------------- builder/openstack/step_allocate_ip.go | 8 +++--- 4 files changed, 29 insertions(+), 30 deletions(-) diff --git a/builder/openstack/builder.go b/builder/openstack/builder.go index 7ff34b869..e0e02edf5 100644 --- a/builder/openstack/builder.go +++ b/builder/openstack/builder.go @@ -126,10 +126,10 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack Wait: b.config.RackconnectWait, }, &StepAllocateIp{ - FloatingIPNetwork: b.config.FloatingIPNetwork, - FloatingIP: b.config.FloatingIP, - ReuseIPs: b.config.ReuseIPs, - InstanceFloatingIPNet: b.config.InstanceFloatingIPNet, + FloatingIPNetwork: b.config.FloatingIPNetwork, + FloatingIP: b.config.FloatingIP, + ReuseIPs: b.config.ReuseIPs, + InstanceFloatingIPNet: b.config.InstanceFloatingIPNet, }, &communicator.StepConnect{ Config: &b.config.RunConfig.Comm, diff --git a/builder/openstack/networks.go b/builder/openstack/networks.go index 0635abc86..537d9ce5a 100644 --- a/builder/openstack/networks.go +++ b/builder/openstack/networks.go @@ -86,13 +86,12 @@ func GetInstancePortID(client *gophercloud.ServiceClient, id string, instance_fl for i := 0; i < len(interfaces); i++ { log.Printf("Instance interface: %v: %+v\n", i, interfaces[i]) if interfaces[i].NetID == instance_float_net { - log.Printf("Found preferred interface: %v\n", i) - selected_interface = i - log.Printf("Using interface value: %v", selected_interface) + log.Printf("Found preferred interface: %v\n", i) + selected_interface = i + log.Printf("Using interface value: %v", selected_interface) } } - return interfaces[selected_interface].PortID, nil } diff --git a/builder/openstack/run_config.go b/builder/openstack/run_config.go index a41382edb..0400d2384 100644 --- a/builder/openstack/run_config.go +++ b/builder/openstack/run_config.go @@ -17,24 +17,24 @@ type RunConfig struct { SSHInterface string `mapstructure:"ssh_interface"` SSHIPVersion string `mapstructure:"ssh_ip_version"` - SourceImage string `mapstructure:"source_image"` - SourceImageName string `mapstructure:"source_image_name"` - SourceImageFilters ImageFilter `mapstructure:"source_image_filter"` - Flavor string `mapstructure:"flavor"` - AvailabilityZone string `mapstructure:"availability_zone"` - RackconnectWait bool `mapstructure:"rackconnect_wait"` - FloatingIPNetwork string `mapstructure:"floating_ip_network"` - FloatingIP string `mapstructure:"floating_ip"` - ReuseIPs bool `mapstructure:"reuse_ips"` - SecurityGroups []string `mapstructure:"security_groups"` - Networks []string `mapstructure:"networks"` - InstanceFloatingIPNet string `mapstructure:"instance_floating_ip_net"` - Ports []string `mapstructure:"ports"` - UserData string `mapstructure:"user_data"` - UserDataFile string `mapstructure:"user_data_file"` - InstanceName string `mapstructure:"instance_name"` - InstanceMetadata map[string]string `mapstructure:"instance_metadata"` - ForceDelete bool `mapstructure:"force_delete"` + SourceImage string `mapstructure:"source_image"` + SourceImageName string `mapstructure:"source_image_name"` + SourceImageFilters ImageFilter `mapstructure:"source_image_filter"` + Flavor string `mapstructure:"flavor"` + AvailabilityZone string `mapstructure:"availability_zone"` + RackconnectWait bool `mapstructure:"rackconnect_wait"` + FloatingIPNetwork string `mapstructure:"floating_ip_network"` + FloatingIP string `mapstructure:"floating_ip"` + ReuseIPs bool `mapstructure:"reuse_ips"` + SecurityGroups []string `mapstructure:"security_groups"` + Networks []string `mapstructure:"networks"` + InstanceFloatingIPNet string `mapstructure:"instance_floating_ip_net"` + Ports []string `mapstructure:"ports"` + UserData string `mapstructure:"user_data"` + UserDataFile string `mapstructure:"user_data_file"` + InstanceName string `mapstructure:"instance_name"` + InstanceMetadata map[string]string `mapstructure:"instance_metadata"` + ForceDelete bool `mapstructure:"force_delete"` ConfigDrive bool `mapstructure:"config_drive"` diff --git a/builder/openstack/step_allocate_ip.go b/builder/openstack/step_allocate_ip.go index 2043cb75c..8b0701491 100644 --- a/builder/openstack/step_allocate_ip.go +++ b/builder/openstack/step_allocate_ip.go @@ -11,10 +11,10 @@ import ( ) type StepAllocateIp struct { - FloatingIPNetwork string - FloatingIP string - ReuseIPs bool - InstanceFloatingIPNet string + FloatingIPNetwork string + FloatingIP string + ReuseIPs bool + InstanceFloatingIPNet string } func (s *StepAllocateIp) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { From c428154c6df66a48360b10ac41a94fbe17d9c9ce Mon Sep 17 00:00:00 2001 From: John-Paul Robinson Date: Tue, 23 Jul 2019 16:34:11 -0500 Subject: [PATCH 3/3] Document instance_floating_ip_net attribute This attribute selects a preferred network on which to associate the instance floating ip. --- website/source/docs/builders/openstack.html.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/website/source/docs/builders/openstack.html.md b/website/source/docs/builders/openstack.html.md index a11780db0..9ae7a9395 100644 --- a/website/source/docs/builders/openstack.html.md +++ b/website/source/docs/builders/openstack.html.md @@ -153,6 +153,13 @@ builder. - `insecure` (boolean) - Whether or not the connection to OpenStack can be done over an insecure connection. By default this is false. +- `instance_floating_ip_net` (string) - The ID of the network to which the + instance is attached and which should be used to associate with the floating + IP. This provides control over the floating ip association on multi-homed + instances. The association otherwise depends on a first-returned-interface + policy which could fail if the network to which it is connected is + unreachable from the floating IP network. + - `key` (string) - Client private key file path for SSL client authentication. If omitted the `OS_KEY` environment variable can be used.