@@ -19,26 +19,42 @@ package clients
1919import (
2020 "encoding/base64"
2121 "fmt"
22+ "time"
2223
2324 "gopkg.in/yaml.v2"
2425 "k8s.io/client-go/kubernetes"
2526
2627 "github.com/gophercloud/gophercloud"
2728 "github.com/gophercloud/gophercloud/openstack"
29+ "github.com/gophercloud/gophercloud/openstack/common/extensions"
30+ "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/attachinterfaces"
2831 "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips"
2932 "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs"
3033 "github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
3134 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
35+ netext "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions"
36+ "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/attributestags"
37+ "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/trunks"
3238 "github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
39+ "github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
3340 "github.com/gophercloud/gophercloud/pagination"
3441 "github.com/gophercloud/utils/openstack/clientconfig"
3542 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3643 "k8s.io/klog"
3744 openstackconfigv1 "sigs.k8s.io/cluster-api-provider-openstack/pkg/apis/openstackproviderconfig/v1alpha1"
3845 clusterv1 "sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1"
46+ "sigs.k8s.io/cluster-api/pkg/util"
3947)
4048
41- const CloudsSecretKey = "clouds.yaml"
49+ const (
50+ CloudsSecretKey = "clouds.yaml"
51+
52+ TimeoutTrunkDelete = 3 * time .Minute
53+ RetryIntervalTrunkDelete = 5 * time .Second
54+
55+ TimeoutPortDelete = 3 * time .Minute
56+ RetryIntervalPortDelete = 5 * time .Second
57+ )
4258
4359type InstanceService struct {
4460 provider * gophercloud.ProviderClient
@@ -238,11 +254,14 @@ func getNetworkIDsByFilter(is *InstanceService, opts *networks.ListOpts) ([]stri
238254 return uuids , nil
239255}
240256
241- func (is * InstanceService ) InstanceCreate (name string , config * openstackconfigv1.OpenstackProviderSpec , cmd string , keyName string ) (instance * Instance , err error ) {
242- var createOpts servers.CreateOpts
257+ func (is * InstanceService ) InstanceCreate (clusterName string , name string , config * openstackconfigv1.OpenstackProviderSpec , cmd string , keyName string ) (instance * Instance , err error ) {
243258 if config == nil {
244259 return nil , fmt .Errorf ("create Options need be specified to create instace" )
245260 }
261+ clusterTags := []string {
262+ "cluster-api-provider-openstack" ,
263+ clusterName ,
264+ }
246265 var nets []servers.Network
247266 for _ , net := range config .Networks {
248267 if net .UUID == "" {
@@ -261,20 +280,98 @@ func (is *InstanceService) InstanceCreate(name string, config *openstackconfigv1
261280 UUID : net .UUID ,
262281 })
263282 }
283+ if len (net .Filter .Tags ) > 0 {
284+ clusterTags = append (clusterTags , net .Filter .Tags )
285+ }
264286 }
265287 userData := base64 .StdEncoding .EncodeToString ([]byte (cmd ))
266- createOpts = servers.CreateOpts {
288+ var ports_list []servers.Network
289+ for _ , net := range nets {
290+ allPages , err := ports .List (is .networkClient , ports.ListOpts {
291+ Name : name ,
292+ NetworkID : net .UUID ,
293+ }).AllPages ()
294+ if err != nil {
295+ return nil , fmt .Errorf ("Searching for existing port for server err: %v" , err )
296+ }
297+ portList , err := ports .ExtractPorts (allPages )
298+ if err != nil {
299+ return nil , fmt .Errorf ("Searching for existing port for server err: %v" , err )
300+ }
301+ var port ports.Port
302+ if len (portList ) == 0 {
303+ // create server port
304+ portCreateOpts := ports.CreateOpts {
305+ Name : name ,
306+ NetworkID : net .UUID ,
307+ SecurityGroups : & config .SecurityGroups ,
308+ }
309+ newPort , err := ports .Create (is .networkClient , portCreateOpts ).Extract ()
310+ if err != nil {
311+ return nil , fmt .Errorf ("Create port for server err: %v" , err )
312+ }
313+ port = * newPort
314+ } else {
315+ port = portList [0 ]
316+ }
317+
318+ _ , err = attributestags .ReplaceAll (is .networkClient , "ports" , port .ID , attributestags.ReplaceAllOpts {
319+ Tags : clusterTags }).Extract ()
320+ if err != nil {
321+ return nil , fmt .Errorf ("Tagging port for server err: %v" , err )
322+ }
323+ ports_list = append (ports_list , servers.Network {
324+ Port : port .ID ,
325+ })
326+
327+ if config .Trunk == true {
328+ allPages , err := trunks .List (is .networkClient , trunks.ListOpts {
329+ Name : name ,
330+ PortID : port .ID ,
331+ }).AllPages ()
332+ if err != nil {
333+ return nil , fmt .Errorf ("Searching for existing trunk for server err: %v" , err )
334+ }
335+ trunkList , err := trunks .ExtractTrunks (allPages )
336+ if err != nil {
337+ return nil , fmt .Errorf ("Searching for existing trunk for server err: %v" , err )
338+ }
339+ var trunk trunks.Trunk
340+ if len (trunkList ) == 0 {
341+ // create trunk with the previous port as parent
342+ trunkCreateOpts := trunks.CreateOpts {
343+ Name : name ,
344+ PortID : port .ID ,
345+ }
346+ newTrunk , err := trunks .Create (is .networkClient , trunkCreateOpts ).Extract ()
347+ if err != nil {
348+ return nil , fmt .Errorf ("Create trunk for server err: %v" , err )
349+ }
350+ trunk = * newTrunk
351+ } else {
352+ trunk = trunkList [0 ]
353+ }
354+
355+ _ , err = attributestags .ReplaceAll (is .networkClient , "trunks" , trunk .ID , attributestags.ReplaceAllOpts {
356+ Tags : clusterTags }).Extract ()
357+ if err != nil {
358+ return nil , fmt .Errorf ("Tagging trunk for server err: %v" , err )
359+ }
360+ }
361+ }
362+
363+ serverCreateOpts := servers.CreateOpts {
267364 Name : name ,
268365 ImageName : config .Image ,
269366 FlavorName : config .Flavor ,
270367 AvailabilityZone : config .AvailabilityZone ,
271- Networks : nets ,
368+ Networks : ports_list ,
272369 UserData : []byte (userData ),
273370 SecurityGroups : config .SecurityGroups ,
274371 ServiceClient : is .computeClient ,
275372 }
276373 server , err := servers .Create (is .computeClient , keypairs.CreateOptsExt {
277- CreateOptsBuilder : createOpts ,
374+ CreateOptsBuilder : serverCreateOpts ,
278375 KeyName : keyName ,
279376 }).Extract ()
280377 if err != nil {
@@ -284,9 +381,91 @@ func (is *InstanceService) InstanceCreate(name string, config *openstackconfigv1
284381}
285382
286383func (is * InstanceService ) InstanceDelete (id string ) error {
384+ // get instance port id
385+ allInterfaces , err := attachinterfaces .List (is .computeClient , id ).AllPages ()
386+ if err != nil {
387+ return err
388+ }
389+ instanceInterfaces , err := attachinterfaces .ExtractInterfaces (allInterfaces )
390+ if err != nil {
391+ return err
392+ }
393+ if len (instanceInterfaces ) < 1 {
394+ return servers .Delete (is .computeClient , id ).ExtractErr ()
395+ }
396+
397+ trunkSupport , err := GetTrunkSupport (is )
398+ if err != nil {
399+ return fmt .Errorf ("Obtaining network extensions err: %v" , err )
400+ }
401+ // get and delete trunks
402+ for _ , port := range instanceInterfaces {
403+ err := attachinterfaces .Delete (is .computeClient , id , port .PortID ).ExtractErr ()
404+ if err != nil {
405+ return err
406+ }
407+ if trunkSupport {
408+ listOpts := trunks.ListOpts {
409+ PortID : port .PortID ,
410+ }
411+ allTrunks , err := trunks .List (is .networkClient , listOpts ).AllPages ()
412+ if err != nil {
413+ return err
414+ }
415+ trunkInfo , err := trunks .ExtractTrunks (allTrunks )
416+ if err != nil {
417+ return err
418+ }
419+ if len (trunkInfo ) == 1 {
420+ err = util .PollImmediate (RetryIntervalTrunkDelete , TimeoutTrunkDelete , func () (bool , error ) {
421+ err := trunks .Delete (is .networkClient , trunkInfo [0 ].ID ).ExtractErr ()
422+ if err != nil {
423+ return false , nil
424+ }
425+ return true , nil
426+ })
427+ if err != nil {
428+ return fmt .Errorf ("Error deleting the trunk %v" , trunkInfo [0 ].ID )
429+ }
430+ }
431+ }
432+
433+ // delete port
434+ err = util .PollImmediate (RetryIntervalPortDelete , TimeoutPortDelete , func () (bool , error ) {
435+ err := ports .Delete (is .networkClient , port .PortID ).ExtractErr ()
436+ if err != nil {
437+ return false , nil
438+ }
439+ return true , nil
440+ })
441+ if err != nil {
442+ return fmt .Errorf ("Error deleting the port %v" , port .PortID )
443+ }
444+ }
445+
446+ // delete instance
287447 return servers .Delete (is .computeClient , id ).ExtractErr ()
288448}
289449
450+ func GetTrunkSupport (is * InstanceService ) (bool , error ) {
451+ allPages , err := netext .List (is .networkClient ).AllPages ()
452+ if err != nil {
453+ return false , err
454+ }
455+
456+ allExts , err := extensions .ExtractExtensions (allPages )
457+ if err != nil {
458+ return false , err
459+ }
460+
461+ for _ , ext := range allExts {
462+ if ext .Alias == "trunk" {
463+ return true , nil
464+ }
465+ }
466+ return false , nil
467+ }
468+
290469func (is * InstanceService ) GetInstanceList (opts * InstanceListOpts ) ([]* Instance , error ) {
291470 var listOpts servers.ListOpts
292471 if opts != nil {
0 commit comments