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

Openstack block device mapping support #7652

Merged
merged 3 commits into from
Oct 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions docs/instance_groups.md
Original file line number Diff line number Diff line change
Expand Up @@ -549,3 +549,28 @@ spec:
minSize: 2
role: Node
```

## Booting from a volume in OpenStack

If you want to boot from a volume when you are running in openstack you can set annotations on the instance groups.

```yaml
# Example for nodes
apiVersion: kops.k8s.io/v1alpha2
kind: InstanceGroup
metadata:
labels:
kops.k8s.io/cluster: k8s.dev.local
name: nodes
annotations:
openstack.kops.io/osVolumeBoot: enabled
openstack.kops.io/osVolumeSize: "15" # In gigabytes
spec:
detailedInstanceMonitoring: true
machineType: t2.medium
maxSize: 2
minSize: 2
role: Node
```

If `openstack.kops.io/osVolumeSize` is not set it will default to the minimum disk specified by the image.
8 changes: 8 additions & 0 deletions pkg/model/openstackmodel/servergroup.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ func (b *ServerGroupModelBuilder) buildInstances(c *fi.ModelBuilderContext, sg *
igMeta[openstack.INSTANCE_GROUP_GENERATION] = fmt.Sprintf("%d", ig.GetGeneration())
igMeta[openstack.CLUSTER_GENERATION] = fmt.Sprintf("%d", b.Cluster.GetGeneration())

if e, ok := ig.ObjectMeta.Annotations[openstack.OS_ANNOTATION+openstack.BOOT_FROM_VOLUME]; ok {
igMeta[openstack.BOOT_FROM_VOLUME] = e
}

if v, ok := ig.ObjectMeta.Annotations[openstack.OS_ANNOTATION+openstack.BOOT_VOLUME_SIZE]; ok {
igMeta[openstack.BOOT_VOLUME_SIZE] = v
}

startupScript, err := b.BootstrapScript.ResourceNodeUp(ig, b.Cluster)
if err != nil {
return fmt.Errorf("Could not create startup script for instance group %s: %v", ig.Name, err)
Expand Down
2 changes: 2 additions & 0 deletions upup/pkg/fi/cloudup/openstack/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ go_library(
"cloud.go",
"dns.go",
"floatingip.go",
"image.go",
"instance.go",
"keypair.go",
"loadbalancer.go",
Expand Down Expand Up @@ -44,6 +45,7 @@ go_library(
"//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors:go_default_library",
Expand Down
16 changes: 14 additions & 2 deletions upup/pkg/fi/cloudup/openstack/cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ import (
"strings"
"time"

"k8s.io/kops/pkg/dns"

"github.com/gophercloud/gophercloud"
os "github.com/gophercloud/gophercloud/openstack"
cinder "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes"
Expand All @@ -36,6 +34,7 @@ import (
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
"github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets"
"github.com/gophercloud/gophercloud/openstack/dns/v2/zones"
"github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
"github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners"
"github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers"
"github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors"
Expand All @@ -54,6 +53,7 @@ import (
"k8s.io/kops/dnsprovider/pkg/dnsprovider/providers/openstack/designate"
"k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/pkg/cloudinstances"
"k8s.io/kops/pkg/dns"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/util/pkg/vfs"
)
Expand Down Expand Up @@ -274,6 +274,8 @@ type OpenstackCloud interface {

GetFloatingIP(id string) (fip *floatingips.FloatingIP, err error)

GetImage(name string) (i *images.Image, err error)

AssociateFloatingIPToInstance(serverID string, opts floatingips.AssociateOpts) (err error)

ListServerFloatingIPs(id string) ([]*string, error)
Expand All @@ -292,6 +294,7 @@ type openstackCloud struct {
novaClient *gophercloud.ServiceClient
dnsClient *gophercloud.ServiceClient
lbClient *gophercloud.ServiceClient
glanceClient *gophercloud.ServiceClient
floatingEnabled bool
extNetworkName *string
extSubnetName *string
Expand Down Expand Up @@ -368,6 +371,14 @@ func NewOpenstackCloud(tags map[string]string, spec *kops.ClusterSpec) (Openstac
return nil, fmt.Errorf("error building nova client: %v", err)
}

glanceClient, err := os.NewImageServiceV2(provider, gophercloud.EndpointOpts{
Type: "image",
Region: region,
})
if err != nil {
return nil, fmt.Errorf("error building glance client: %v", err)
}

var dnsClient *gophercloud.ServiceClient
if !dns.IsGossipHostname(tags[TagClusterName]) {
//TODO: This should be replaced with the environment variable methods as done above
Expand All @@ -387,6 +398,7 @@ func NewOpenstackCloud(tags map[string]string, spec *kops.ClusterSpec) (Openstac
neutronClient: neutronClient,
novaClient: novaClient,
dnsClient: dnsClient,
glanceClient: glanceClient,
tags: tags,
region: region,
useOctavia: false,
Expand Down
46 changes: 46 additions & 0 deletions upup/pkg/fi/cloudup/openstack/image.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package openstack

import (
"fmt"

"github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
)

func (c *openstackCloud) GetImage(name string) (*images.Image, error) {
opts := images.ListOpts{Name: name}
pager := images.List(c.glanceClient, opts)
page, err := pager.AllPages()
if err != nil {
return nil, fmt.Errorf("failed to list images: %v", err)
}

i, err := images.ExtractImages(page)
if err != nil {
return nil, fmt.Errorf("failed to extract images: %v", err)
}

switch len(i) {
case 1:
return &i[0], nil
case 0:
return nil, fmt.Errorf("no image found with name %v", name)
default:
return nil, fmt.Errorf("multiple images found with name %v", name)
}
}
3 changes: 3 additions & 0 deletions upup/pkg/fi/cloudup/openstack/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ import (
const (
INSTANCE_GROUP_GENERATION = "ig_generation"
CLUSTER_GENERATION = "cluster_generation"
OS_ANNOTATION = "openstack.kops.io/"
BOOT_FROM_VOLUME = "osVolumeBoot"
BOOT_VOLUME_SIZE = "osVolumeSize"
)

// floatingBackoff is the backoff strategy for listing openstack floatingips
Expand Down
1 change: 1 addition & 0 deletions upup/pkg/fi/cloudup/openstacktasks/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ go_library(
"//util/pkg/vfs:go_default_library",
"//vendor/github.com/gophercloud/gophercloud:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/schedulerhints:go_default_library",
Expand Down
58 changes: 57 additions & 1 deletion upup/pkg/fi/cloudup/openstacktasks/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ package openstacktasks

import (
"fmt"
"strconv"

"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/schedulerhints"
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
Expand Down Expand Up @@ -183,7 +185,13 @@ func (_ *Instance) RenderOpenstack(t *openstack.OpenstackAPITarget, a, e, change
Group: *e.ServerGroup.ID,
},
}
v, err := t.Cloud.CreateInstance(sgext)

opts, err := includeBootVolumeOptions(t, e, sgext)
if err != nil {
return err
}

v, err := t.Cloud.CreateInstance(opts)
if err != nil {
return fmt.Errorf("Error creating instance: %v", err)
}
Expand All @@ -198,3 +206,51 @@ func (_ *Instance) RenderOpenstack(t *openstack.OpenstackAPITarget, a, e, change
klog.V(2).Infof("Openstack task Instance::RenderOpenstack did nothing")
return nil
}

func includeBootVolumeOptions(t *openstack.OpenstackAPITarget, e *Instance, opts servers.CreateOptsBuilder) (servers.CreateOptsBuilder, error) {
if !bootFromVolume(e.Metadata) {
return opts, nil
}

i, err := t.Cloud.GetImage(fi.StringValue(e.Image))
if err != nil {
return nil, fmt.Errorf("Error getting image information: %v", err)
}

bfv := bootfromvolume.CreateOptsExt{
CreateOptsBuilder: opts,
BlockDevice: []bootfromvolume.BlockDevice{{
BootIndex: 0,
DeleteOnTermination: true,
DestinationType: "volume",
SourceType: "image",
UUID: i.ID,
VolumeSize: i.MinDiskGigabytes,
}},
}

if s, ok := e.Metadata[openstack.BOOT_VOLUME_SIZE]; ok {
i, err := strconv.ParseInt(s, 10, 64)
if err != nil {
return nil, fmt.Errorf("Invalid value for %v: %v", openstack.BOOT_VOLUME_SIZE, err)
}

bfv.BlockDevice[0].VolumeSize = int(i)
}

return bfv, nil
}

func bootFromVolume(m map[string]string) bool {
v, ok := m[openstack.BOOT_FROM_VOLUME]
if !ok {
return false
}

switch v {
case "true", "enabled":
return true
default:
return false
}
}

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

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

34 changes: 34 additions & 0 deletions vendor/github.com/gophercloud/gophercloud/internal/util.go

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

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

Loading