mirror of https://github.com/kubernetes/kops.git
add keypair role
This commit is contained in:
parent
02daea62ae
commit
ed8d03d402
|
|
@ -483,6 +483,10 @@
|
||||||
".",
|
".",
|
||||||
"openstack",
|
"openstack",
|
||||||
"openstack/blockstorage/v2/volumes",
|
"openstack/blockstorage/v2/volumes",
|
||||||
|
"openstack/compute/v2/extensions/keypairs",
|
||||||
|
"openstack/compute/v2/flavors",
|
||||||
|
"openstack/compute/v2/images",
|
||||||
|
"openstack/compute/v2/servers",
|
||||||
"openstack/identity/v2/tenants",
|
"openstack/identity/v2/tenants",
|
||||||
"openstack/identity/v2/tokens",
|
"openstack/identity/v2/tokens",
|
||||||
"openstack/identity/v3/tokens",
|
"openstack/identity/v3/tokens",
|
||||||
|
|
@ -1706,6 +1710,6 @@
|
||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
inputs-digest = "33c4809d9ffb5425549fab80b4edb6a9fa84726ca461e8caa314b12b27f61a8d"
|
inputs-digest = "4708af635b2bbadafcb67be11520a8a6b34557b2f2ad2fec6f80b39a5ca646ad"
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
solver-version = 1
|
solver-version = 1
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ go_library(
|
||||||
"context.go",
|
"context.go",
|
||||||
"convenience.go",
|
"convenience.go",
|
||||||
"network.go",
|
"network.go",
|
||||||
|
"sshkey.go",
|
||||||
],
|
],
|
||||||
importpath = "k8s.io/kops/pkg/model/openstackmodel",
|
importpath = "k8s.io/kops/pkg/model/openstackmodel",
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016 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 openstackmodel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"k8s.io/kops/upup/pkg/fi"
|
||||||
|
"k8s.io/kops/upup/pkg/fi/cloudup/openstacktasks"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SSHKeyModelBuilder configures SSH objects
|
||||||
|
type SSHKeyModelBuilder struct {
|
||||||
|
*OpenstackModelContext
|
||||||
|
Lifecycle *fi.Lifecycle
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ fi.ModelBuilder = &SSHKeyModelBuilder{}
|
||||||
|
|
||||||
|
func (b *SSHKeyModelBuilder) Build(c *fi.ModelBuilderContext) error {
|
||||||
|
name, err := b.SSHKeyName()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t := &openstacktasks.SSHKey{
|
||||||
|
Name: s(name),
|
||||||
|
Lifecycle: b.Lifecycle,
|
||||||
|
PublicKey: fi.WrapResource(fi.NewStringResource(string(b.SSHPublicKeys[0]))),
|
||||||
|
}
|
||||||
|
c.AddTask(t)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -463,10 +463,21 @@ func (c *ApplyClusterCmd) Run() error {
|
||||||
region = osCloud.Region()
|
region = osCloud.Region()
|
||||||
|
|
||||||
l.AddTypes(map[string]interface{}{
|
l.AddTypes(map[string]interface{}{
|
||||||
|
"sshKey": &openstacktasks.SSHKey{},
|
||||||
// Networking
|
// Networking
|
||||||
"network": &openstacktasks.Network{},
|
"network": &openstacktasks.Network{},
|
||||||
"router": &openstacktasks.Router{},
|
"router": &openstacktasks.Router{},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if len(sshPublicKeys) == 0 {
|
||||||
|
return fmt.Errorf("SSH public key must be specified when running with Openstack (create with `kops create secret --name %s sshpublickey admin -i ~/.ssh/id_rsa.pub`)", cluster.ObjectMeta.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
modelContext.SSHPublicKeys = sshPublicKeys
|
||||||
|
|
||||||
|
if len(sshPublicKeys) != 1 {
|
||||||
|
return fmt.Errorf("Exactly one 'admin' SSH public key can be specified when running with Openstack; please delete a key using `kops delete secret`")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unknown CloudProvider %q", cluster.Spec.CloudProvider)
|
return fmt.Errorf("unknown CloudProvider %q", cluster.Spec.CloudProvider)
|
||||||
|
|
@ -604,6 +615,7 @@ func (c *ApplyClusterCmd) Run() error {
|
||||||
|
|
||||||
l.Builders = append(l.Builders,
|
l.Builders = append(l.Builders,
|
||||||
&openstackmodel.NetworkModelBuilder{OpenstackModelContext: openstackModelContext, Lifecycle: &networkLifecycle},
|
&openstackmodel.NetworkModelBuilder{OpenstackModelContext: openstackModelContext, Lifecycle: &networkLifecycle},
|
||||||
|
&openstackmodel.SSHKeyModelBuilder{OpenstackModelContext: openstackModelContext, Lifecycle: &securityLifecycle},
|
||||||
)
|
)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ go_library(
|
||||||
"//vendor/github.com/gophercloud/gophercloud:go_default_library",
|
"//vendor/github.com/gophercloud/gophercloud:go_default_library",
|
||||||
"//vendor/github.com/gophercloud/gophercloud/openstack:go_default_library",
|
"//vendor/github.com/gophercloud/gophercloud/openstack:go_default_library",
|
||||||
"//vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes:go_default_library",
|
"//vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes:go_default_library",
|
||||||
|
"//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs:go_default_library",
|
||||||
"//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers:go_default_library",
|
"//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers:go_default_library",
|
||||||
"//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups:go_default_library",
|
"//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups:go_default_library",
|
||||||
"//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules:go_default_library",
|
"//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules:go_default_library",
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"github.com/gophercloud/gophercloud"
|
"github.com/gophercloud/gophercloud"
|
||||||
os "github.com/gophercloud/gophercloud/openstack"
|
os "github.com/gophercloud/gophercloud/openstack"
|
||||||
cinder "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes"
|
cinder "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs"
|
||||||
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers"
|
||||||
sg "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups"
|
sg "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups"
|
||||||
sgr "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules"
|
sgr "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules"
|
||||||
|
|
@ -42,6 +43,9 @@ const TagNameEtcdClusterPrefix = "k8s.io/etcd/"
|
||||||
const TagNameRolePrefix = "k8s.io/role/"
|
const TagNameRolePrefix = "k8s.io/role/"
|
||||||
const TagClusterName = "KubernetesCluster"
|
const TagClusterName = "KubernetesCluster"
|
||||||
|
|
||||||
|
// ErrNotFound is used to inform that the object is not found
|
||||||
|
var ErrNotFound = "Resource not found"
|
||||||
|
|
||||||
// readBackoff is the backoff strategy for openstack read retries.
|
// readBackoff is the backoff strategy for openstack read retries.
|
||||||
var readBackoff = wait.Backoff{
|
var readBackoff = wait.Backoff{
|
||||||
Duration: time.Second,
|
Duration: time.Second,
|
||||||
|
|
@ -105,11 +109,18 @@ type OpenstackCloud interface {
|
||||||
|
|
||||||
//CreateSubnet will create a new Neutron subnet
|
//CreateSubnet will create a new Neutron subnet
|
||||||
CreateSubnet(opt subnets.CreateOptsBuilder) (*subnets.Subnet, error)
|
CreateSubnet(opt subnets.CreateOptsBuilder) (*subnets.Subnet, error)
|
||||||
|
|
||||||
|
// ListKeypair will return the Nova keypairs
|
||||||
|
ListKeypair(name string) (*keypairs.KeyPair, error)
|
||||||
|
|
||||||
|
// CreateKeypair will create a new Nova Keypair
|
||||||
|
CreateKeypair(opt keypairs.CreateOptsBuilder) (*keypairs.KeyPair, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type openstackCloud struct {
|
type openstackCloud struct {
|
||||||
cinderClient *gophercloud.ServiceClient
|
cinderClient *gophercloud.ServiceClient
|
||||||
neutronClient *gophercloud.ServiceClient
|
neutronClient *gophercloud.ServiceClient
|
||||||
|
novaClient *gophercloud.ServiceClient
|
||||||
tags map[string]string
|
tags map[string]string
|
||||||
region string
|
region string
|
||||||
}
|
}
|
||||||
|
|
@ -145,11 +156,22 @@ func NewOpenstackCloud(tags map[string]string) (OpenstackCloud, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error building neutron client: %v", err)
|
return nil, fmt.Errorf("error building neutron client: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
endpointOpt, err = config.GetServiceConfig("Nova")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
novaClient, err := os.NewComputeV2(provider, endpointOpt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error building nova client: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
region := endpointOpt.Region
|
region := endpointOpt.Region
|
||||||
|
|
||||||
c := &openstackCloud{
|
c := &openstackCloud{
|
||||||
cinderClient: cinderClient,
|
cinderClient: cinderClient,
|
||||||
neutronClient: neutronClient,
|
neutronClient: neutronClient,
|
||||||
|
novaClient: novaClient,
|
||||||
tags: tags,
|
tags: tags,
|
||||||
region: region,
|
region: region,
|
||||||
}
|
}
|
||||||
|
|
@ -483,3 +505,45 @@ func (c *openstackCloud) CreateSubnet(opt subnets.CreateOptsBuilder) (*subnets.S
|
||||||
return s, wait.ErrWaitTimeout
|
return s, wait.ErrWaitTimeout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *openstackCloud) ListKeypair(name string) (*keypairs.KeyPair, error) {
|
||||||
|
var k *keypairs.KeyPair
|
||||||
|
done, err := vfs.RetryWithBackoff(readBackoff, func() (bool, error) {
|
||||||
|
rs, err := keypairs.Get(c.novaClient, name).Extract()
|
||||||
|
if err != nil {
|
||||||
|
if err.Error() == ErrNotFound {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, fmt.Errorf("error listing keypair: %v", err)
|
||||||
|
}
|
||||||
|
k = rs
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return k, err
|
||||||
|
} else if done {
|
||||||
|
return k, nil
|
||||||
|
} else {
|
||||||
|
return k, wait.ErrWaitTimeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *openstackCloud) CreateKeypair(opt keypairs.CreateOptsBuilder) (*keypairs.KeyPair, error) {
|
||||||
|
var k *keypairs.KeyPair
|
||||||
|
|
||||||
|
done, err := vfs.RetryWithBackoff(writeBackoff, func() (bool, error) {
|
||||||
|
v, err := keypairs.Create(c.novaClient, opt).Extract()
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("error creating keypair: %v", err)
|
||||||
|
}
|
||||||
|
k = v
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return k, err
|
||||||
|
} else if done {
|
||||||
|
return k, nil
|
||||||
|
} else {
|
||||||
|
return k, wait.ErrWaitTimeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ go_library(
|
||||||
"securitygroup.go",
|
"securitygroup.go",
|
||||||
"securitygroup_fitask.go",
|
"securitygroup_fitask.go",
|
||||||
"securitygrouprule.go",
|
"securitygrouprule.go",
|
||||||
|
"sshkey.go",
|
||||||
|
"sshkey_fitask.go",
|
||||||
"subnet.go",
|
"subnet.go",
|
||||||
"subnet_fitask.go",
|
"subnet_fitask.go",
|
||||||
"volume.go",
|
"volume.go",
|
||||||
|
|
@ -18,11 +20,13 @@ go_library(
|
||||||
importpath = "k8s.io/kops/upup/pkg/fi/cloudup/openstacktasks",
|
importpath = "k8s.io/kops/upup/pkg/fi/cloudup/openstacktasks",
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
deps = [
|
||||||
|
"//pkg/pki:go_default_library",
|
||||||
"//upup/pkg/fi:go_default_library",
|
"//upup/pkg/fi:go_default_library",
|
||||||
"//upup/pkg/fi/cloudup/openstack:go_default_library",
|
"//upup/pkg/fi/cloudup/openstack:go_default_library",
|
||||||
"//vendor/github.com/golang/glog:go_default_library",
|
"//vendor/github.com/golang/glog:go_default_library",
|
||||||
"//vendor/github.com/gophercloud/gophercloud: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/blockstorage/v2/volumes:go_default_library",
|
||||||
|
"//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs:go_default_library",
|
||||||
"//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers:go_default_library",
|
"//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers:go_default_library",
|
||||||
"//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups:go_default_library",
|
"//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups:go_default_library",
|
||||||
"//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules:go_default_library",
|
"//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules:go_default_library",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,132 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016 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 openstacktasks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/golang/glog"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs"
|
||||||
|
|
||||||
|
"k8s.io/kops/pkg/pki"
|
||||||
|
"k8s.io/kops/upup/pkg/fi"
|
||||||
|
"k8s.io/kops/upup/pkg/fi/cloudup/openstack"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate fitask -type=SSHKey
|
||||||
|
type SSHKey struct {
|
||||||
|
Name *string
|
||||||
|
Lifecycle *fi.Lifecycle
|
||||||
|
|
||||||
|
PublicKey *fi.ResourceHolder
|
||||||
|
|
||||||
|
KeyFingerprint *string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ fi.CompareWithID = &SSHKey{}
|
||||||
|
|
||||||
|
func (e *SSHKey) CompareWithID() *string {
|
||||||
|
return e.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SSHKey) Find(c *fi.Context) (*SSHKey, error) {
|
||||||
|
cloud := c.Cloud.(openstack.OpenstackCloud)
|
||||||
|
rs, err := cloud.ListKeypair(openstackKeyPairName(fi.StringValue(e.Name)))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if rs == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
actual := &SSHKey{
|
||||||
|
Name: e.Name,
|
||||||
|
KeyFingerprint: fi.String(rs.Fingerprint),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avoid spurious changes
|
||||||
|
if fi.StringValue(actual.KeyFingerprint) == fi.StringValue(e.KeyFingerprint) {
|
||||||
|
glog.V(2).Infof("SSH key fingerprints match; assuming public keys match")
|
||||||
|
actual.PublicKey = e.PublicKey
|
||||||
|
} else {
|
||||||
|
glog.V(2).Infof("Computed SSH key fingerprint mismatch: %q %q", fi.StringValue(e.KeyFingerprint), fi.StringValue(actual.KeyFingerprint))
|
||||||
|
}
|
||||||
|
actual.Lifecycle = e.Lifecycle
|
||||||
|
return actual, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SSHKey) Run(c *fi.Context) error {
|
||||||
|
if e.KeyFingerprint == nil && e.PublicKey != nil {
|
||||||
|
publicKey, err := e.PublicKey.AsString()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error reading SSH public key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
keyFingerprint, err := pki.ComputeAWSKeyFingerprint(publicKey)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error computing key fingerprint for SSH key: %v", err)
|
||||||
|
}
|
||||||
|
glog.V(2).Infof("Computed SSH key fingerprint as %q", keyFingerprint)
|
||||||
|
e.KeyFingerprint = &keyFingerprint
|
||||||
|
}
|
||||||
|
return fi.DefaultDeltaRunMethod(e, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SSHKey) CheckChanges(a, e, changes *SSHKey) error {
|
||||||
|
if a != nil {
|
||||||
|
if changes.Name != nil {
|
||||||
|
return fi.CannotChangeField("Name")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func openstackKeyPairName(org string) string {
|
||||||
|
name := strings.Replace(org, ".", "-", -1)
|
||||||
|
name = strings.Replace(name, ":", "_", -1)
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_ *SSHKey) RenderOpenstack(t *openstack.OpenstackAPITarget, a, e, changes *SSHKey) error {
|
||||||
|
if a == nil {
|
||||||
|
glog.V(2).Infof("Creating Keypair with name:%q", fi.StringValue(e.Name))
|
||||||
|
|
||||||
|
opt := keypairs.CreateOpts{
|
||||||
|
Name: openstackKeyPairName(fi.StringValue(e.Name)),
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.PublicKey != nil {
|
||||||
|
d, err := e.PublicKey.AsString()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error rendering SSHKey PublicKey: %v", err)
|
||||||
|
}
|
||||||
|
opt.PublicKey = d
|
||||||
|
}
|
||||||
|
|
||||||
|
v, err := t.Cloud.CreateKeypair(opt)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error creating keypair: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
e.KeyFingerprint = fi.String(v.Fingerprint)
|
||||||
|
glog.V(2).Infof("Creating a new Openstack keypair, id=%s", v.Fingerprint)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
e.KeyFingerprint = a.KeyFingerprint
|
||||||
|
glog.V(2).Infof("Using an existing Openstack keypair, id=%s", fi.StringValue(e.KeyFingerprint))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
Copyright 2018 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by ""fitask" -type=SSHKey"; DO NOT EDIT
|
||||||
|
|
||||||
|
package openstacktasks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"k8s.io/kops/upup/pkg/fi"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SSHKey
|
||||||
|
|
||||||
|
// JSON marshalling boilerplate
|
||||||
|
type realSSHKey SSHKey
|
||||||
|
|
||||||
|
// UnmarshalJSON implements conversion to JSON, supporitng an alternate specification of the object as a string
|
||||||
|
func (o *SSHKey) UnmarshalJSON(data []byte) error {
|
||||||
|
var jsonName string
|
||||||
|
if err := json.Unmarshal(data, &jsonName); err == nil {
|
||||||
|
o.Name = &jsonName
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var r realSSHKey
|
||||||
|
if err := json.Unmarshal(data, &r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*o = SSHKey(r)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ fi.HasLifecycle = &SSHKey{}
|
||||||
|
|
||||||
|
// GetLifecycle returns the Lifecycle of the object, implementing fi.HasLifecycle
|
||||||
|
func (o *SSHKey) GetLifecycle() *fi.Lifecycle {
|
||||||
|
return o.Lifecycle
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLifecycle sets the Lifecycle of the object, implementing fi.SetLifecycle
|
||||||
|
func (o *SSHKey) SetLifecycle(lifecycle fi.Lifecycle) {
|
||||||
|
o.Lifecycle = &lifecycle
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ fi.HasName = &SSHKey{}
|
||||||
|
|
||||||
|
// GetName returns the Name of the object, implementing fi.HasName
|
||||||
|
func (o *SSHKey) GetName() *string {
|
||||||
|
return o.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetName sets the Name of the object, implementing fi.SetName
|
||||||
|
func (o *SSHKey) SetName(name string) {
|
||||||
|
o.Name = &name
|
||||||
|
}
|
||||||
|
|
||||||
|
// String is the stringer function for the task, producing readable output using fi.TaskAsString
|
||||||
|
func (o *SSHKey) String() string {
|
||||||
|
return fi.TaskAsString(o)
|
||||||
|
}
|
||||||
19
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/BUILD.bazel
generated
vendored
Normal file
19
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/BUILD.bazel
generated
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = [
|
||||||
|
"doc.go",
|
||||||
|
"requests.go",
|
||||||
|
"results.go",
|
||||||
|
"urls.go",
|
||||||
|
],
|
||||||
|
importmap = "vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs",
|
||||||
|
importpath = "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
"//vendor/github.com/gophercloud/gophercloud:go_default_library",
|
||||||
|
"//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers:go_default_library",
|
||||||
|
"//vendor/github.com/gophercloud/gophercloud/pagination:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
||||||
71
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/doc.go
generated
vendored
Normal file
71
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
Package keypairs provides the ability to manage key pairs as well as create
|
||||||
|
servers with a specified key pair.
|
||||||
|
|
||||||
|
Example to List Key Pairs
|
||||||
|
|
||||||
|
allPages, err := keypairs.List(computeClient).AllPages()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
allKeyPairs, err := keypairs.ExtractKeyPairs(allPages)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, kp := range allKeyPairs {
|
||||||
|
fmt.Printf("%+v\n", kp)
|
||||||
|
}
|
||||||
|
|
||||||
|
Example to Create a Key Pair
|
||||||
|
|
||||||
|
createOpts := keypairs.CreateOpts{
|
||||||
|
Name: "keypair-name",
|
||||||
|
}
|
||||||
|
|
||||||
|
keypair, err := keypairs.Create(computeClient, createOpts).Extract()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%+v", keypair)
|
||||||
|
|
||||||
|
Example to Import a Key Pair
|
||||||
|
|
||||||
|
createOpts := keypairs.CreateOpts{
|
||||||
|
Name: "keypair-name",
|
||||||
|
PublicKey: "public-key",
|
||||||
|
}
|
||||||
|
|
||||||
|
keypair, err := keypairs.Create(computeClient, createOpts).Extract()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
Example to Delete a Key Pair
|
||||||
|
|
||||||
|
err := keypairs.Delete(computeClient, "keypair-name").ExtractErr()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
Example to Create a Server With a Key Pair
|
||||||
|
|
||||||
|
serverCreateOpts := servers.CreateOpts{
|
||||||
|
Name: "server_name",
|
||||||
|
ImageRef: "image-uuid",
|
||||||
|
FlavorRef: "flavor-uuid",
|
||||||
|
}
|
||||||
|
|
||||||
|
createOpts := keypairs.CreateOpts{
|
||||||
|
CreateOptsBuilder: serverCreateOpts,
|
||||||
|
KeyName: "keypair-name",
|
||||||
|
}
|
||||||
|
|
||||||
|
server, err := servers.Create(computeClient, createOpts).Extract()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
package keypairs
|
||||||
86
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/requests.go
generated
vendored
Normal file
86
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/requests.go
generated
vendored
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
package keypairs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
|
||||||
|
"github.com/gophercloud/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateOptsExt adds a KeyPair option to the base CreateOpts.
|
||||||
|
type CreateOptsExt struct {
|
||||||
|
servers.CreateOptsBuilder
|
||||||
|
|
||||||
|
// KeyName is the name of the key pair.
|
||||||
|
KeyName string `json:"key_name,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToServerCreateMap adds the key_name to the base server creation options.
|
||||||
|
func (opts CreateOptsExt) ToServerCreateMap() (map[string]interface{}, error) {
|
||||||
|
base, err := opts.CreateOptsBuilder.ToServerCreateMap()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.KeyName == "" {
|
||||||
|
return base, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
serverMap := base["server"].(map[string]interface{})
|
||||||
|
serverMap["key_name"] = opts.KeyName
|
||||||
|
|
||||||
|
return base, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// List returns a Pager that allows you to iterate over a collection of KeyPairs.
|
||||||
|
func List(client *gophercloud.ServiceClient) pagination.Pager {
|
||||||
|
return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page {
|
||||||
|
return KeyPairPage{pagination.SinglePageBase(r)}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOptsBuilder allows extensions to add additional parameters to the
|
||||||
|
// Create request.
|
||||||
|
type CreateOptsBuilder interface {
|
||||||
|
ToKeyPairCreateMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOpts specifies KeyPair creation or import parameters.
|
||||||
|
type CreateOpts struct {
|
||||||
|
// Name is a friendly name to refer to this KeyPair in other services.
|
||||||
|
Name string `json:"name" required:"true"`
|
||||||
|
|
||||||
|
// PublicKey [optional] is a pregenerated OpenSSH-formatted public key.
|
||||||
|
// If provided, this key will be imported and no new key will be created.
|
||||||
|
PublicKey string `json:"public_key,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToKeyPairCreateMap constructs a request body from CreateOpts.
|
||||||
|
func (opts CreateOpts) ToKeyPairCreateMap() (map[string]interface{}, error) {
|
||||||
|
return gophercloud.BuildRequestBody(opts, "keypair")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create requests the creation of a new KeyPair on the server, or to import a
|
||||||
|
// pre-existing keypair.
|
||||||
|
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
|
||||||
|
b, err := opts.ToKeyPairCreateMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns public data about a previously uploaded KeyPair.
|
||||||
|
func Get(client *gophercloud.ServiceClient, name string) (r GetResult) {
|
||||||
|
_, r.Err = client.Get(getURL(client, name), &r.Body, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete requests the deletion of a previous stored KeyPair from the server.
|
||||||
|
func Delete(client *gophercloud.ServiceClient, name string) (r DeleteResult) {
|
||||||
|
_, r.Err = client.Delete(deleteURL(client, name), nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
91
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/results.go
generated
vendored
Normal file
91
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/results.go
generated
vendored
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
package keypairs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// KeyPair is an SSH key known to the OpenStack Cloud that is available to be
|
||||||
|
// injected into servers.
|
||||||
|
type KeyPair struct {
|
||||||
|
// Name is used to refer to this keypair from other services within this
|
||||||
|
// region.
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// Fingerprint is a short sequence of bytes that can be used to authenticate
|
||||||
|
// or validate a longer public key.
|
||||||
|
Fingerprint string `json:"fingerprint"`
|
||||||
|
|
||||||
|
// PublicKey is the public key from this pair, in OpenSSH format.
|
||||||
|
// "ssh-rsa AAAAB3Nz..."
|
||||||
|
PublicKey string `json:"public_key"`
|
||||||
|
|
||||||
|
// PrivateKey is the private key from this pair, in PEM format.
|
||||||
|
// "-----BEGIN RSA PRIVATE KEY-----\nMIICXA..."
|
||||||
|
// It is only present if this KeyPair was just returned from a Create call.
|
||||||
|
PrivateKey string `json:"private_key"`
|
||||||
|
|
||||||
|
// UserID is the user who owns this KeyPair.
|
||||||
|
UserID string `json:"user_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyPairPage stores a single page of all KeyPair results from a List call.
|
||||||
|
// Use the ExtractKeyPairs function to convert the results to a slice of
|
||||||
|
// KeyPairs.
|
||||||
|
type KeyPairPage struct {
|
||||||
|
pagination.SinglePageBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty determines whether or not a KeyPairPage is empty.
|
||||||
|
func (page KeyPairPage) IsEmpty() (bool, error) {
|
||||||
|
ks, err := ExtractKeyPairs(page)
|
||||||
|
return len(ks) == 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractKeyPairs interprets a page of results as a slice of KeyPairs.
|
||||||
|
func ExtractKeyPairs(r pagination.Page) ([]KeyPair, error) {
|
||||||
|
type pair struct {
|
||||||
|
KeyPair KeyPair `json:"keypair"`
|
||||||
|
}
|
||||||
|
var s struct {
|
||||||
|
KeyPairs []pair `json:"keypairs"`
|
||||||
|
}
|
||||||
|
err := (r.(KeyPairPage)).ExtractInto(&s)
|
||||||
|
results := make([]KeyPair, len(s.KeyPairs))
|
||||||
|
for i, pair := range s.KeyPairs {
|
||||||
|
results[i] = pair.KeyPair
|
||||||
|
}
|
||||||
|
return results, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type keyPairResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract is a method that attempts to interpret any KeyPair resource response
|
||||||
|
// as a KeyPair struct.
|
||||||
|
func (r keyPairResult) Extract() (*KeyPair, error) {
|
||||||
|
var s struct {
|
||||||
|
KeyPair *KeyPair `json:"keypair"`
|
||||||
|
}
|
||||||
|
err := r.ExtractInto(&s)
|
||||||
|
return s.KeyPair, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateResult is the response from a Create operation. Call its Extract method
|
||||||
|
// to interpret it as a KeyPair.
|
||||||
|
type CreateResult struct {
|
||||||
|
keyPairResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetResult is the response from a Get operation. Call its Extract method to
|
||||||
|
// interpret it as a KeyPair.
|
||||||
|
type GetResult struct {
|
||||||
|
keyPairResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteResult is the response from a Delete operation. Call its ExtractErr
|
||||||
|
// method to determine if the call succeeded or failed.
|
||||||
|
type DeleteResult struct {
|
||||||
|
gophercloud.ErrResult
|
||||||
|
}
|
||||||
25
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/urls.go
generated
vendored
Normal file
25
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/urls.go
generated
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
package keypairs
|
||||||
|
|
||||||
|
import "github.com/gophercloud/gophercloud"
|
||||||
|
|
||||||
|
const resourcePath = "os-keypairs"
|
||||||
|
|
||||||
|
func resourceURL(c *gophercloud.ServiceClient) string {
|
||||||
|
return c.ServiceURL(resourcePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func listURL(c *gophercloud.ServiceClient) string {
|
||||||
|
return resourceURL(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createURL(c *gophercloud.ServiceClient) string {
|
||||||
|
return resourceURL(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getURL(c *gophercloud.ServiceClient, name string) string {
|
||||||
|
return c.ServiceURL(resourcePath, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteURL(c *gophercloud.ServiceClient, name string) string {
|
||||||
|
return getURL(c, name)
|
||||||
|
}
|
||||||
18
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/BUILD.bazel
generated
vendored
Normal file
18
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/BUILD.bazel
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = [
|
||||||
|
"doc.go",
|
||||||
|
"requests.go",
|
||||||
|
"results.go",
|
||||||
|
"urls.go",
|
||||||
|
],
|
||||||
|
importmap = "vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors",
|
||||||
|
importpath = "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
"//vendor/github.com/gophercloud/gophercloud:go_default_library",
|
||||||
|
"//vendor/github.com/gophercloud/gophercloud/pagination:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
||||||
137
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/doc.go
generated
vendored
Normal file
137
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,137 @@
|
||||||
|
/*
|
||||||
|
Package flavors provides information and interaction with the flavor API
|
||||||
|
in the OpenStack Compute service.
|
||||||
|
|
||||||
|
A flavor is an available hardware configuration for a server. Each flavor
|
||||||
|
has a unique combination of disk space, memory capacity and priority for CPU
|
||||||
|
time.
|
||||||
|
|
||||||
|
Example to List Flavors
|
||||||
|
|
||||||
|
listOpts := flavors.ListOpts{
|
||||||
|
AccessType: flavors.PublicAccess,
|
||||||
|
}
|
||||||
|
|
||||||
|
allPages, err := flavors.ListDetail(computeClient, listOpts).AllPages()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
allFlavors, err := flavors.ExtractFlavors(allPages)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, flavor := range allFlavors {
|
||||||
|
fmt.Printf("%+v\n", flavor)
|
||||||
|
}
|
||||||
|
|
||||||
|
Example to Create a Flavor
|
||||||
|
|
||||||
|
createOpts := flavors.CreateOpts{
|
||||||
|
ID: "1",
|
||||||
|
Name: "m1.tiny",
|
||||||
|
Disk: gophercloud.IntToPointer(1),
|
||||||
|
RAM: 512,
|
||||||
|
VCPUs: 1,
|
||||||
|
RxTxFactor: 1.0,
|
||||||
|
}
|
||||||
|
|
||||||
|
flavor, err := flavors.Create(computeClient, createOpts).Extract()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
Example to List Flavor Access
|
||||||
|
|
||||||
|
flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b"
|
||||||
|
|
||||||
|
allPages, err := flavors.ListAccesses(computeClient, flavorID).AllPages()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
allAccesses, err := flavors.ExtractAccesses(allPages)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, access := range allAccesses {
|
||||||
|
fmt.Printf("%+v", access)
|
||||||
|
}
|
||||||
|
|
||||||
|
Example to Grant Access to a Flavor
|
||||||
|
|
||||||
|
flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b"
|
||||||
|
|
||||||
|
accessOpts := flavors.AddAccessOpts{
|
||||||
|
Tenant: "15153a0979884b59b0592248ef947921",
|
||||||
|
}
|
||||||
|
|
||||||
|
accessList, err := flavors.AddAccess(computeClient, flavor.ID, accessOpts).Extract()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
Example to Remove/Revoke Access to a Flavor
|
||||||
|
|
||||||
|
flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b"
|
||||||
|
|
||||||
|
accessOpts := flavors.RemoveAccessOpts{
|
||||||
|
Tenant: "15153a0979884b59b0592248ef947921",
|
||||||
|
}
|
||||||
|
|
||||||
|
accessList, err := flavors.RemoveAccess(computeClient, flavor.ID, accessOpts).Extract()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
Example to Create Extra Specs for a Flavor
|
||||||
|
|
||||||
|
flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b"
|
||||||
|
|
||||||
|
createOpts := flavors.ExtraSpecsOpts{
|
||||||
|
"hw:cpu_policy": "CPU-POLICY",
|
||||||
|
"hw:cpu_thread_policy": "CPU-THREAD-POLICY",
|
||||||
|
}
|
||||||
|
createdExtraSpecs, err := flavors.CreateExtraSpecs(computeClient, flavorID, createOpts).Extract()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%+v", createdExtraSpecs)
|
||||||
|
|
||||||
|
Example to Get Extra Specs for a Flavor
|
||||||
|
|
||||||
|
flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b"
|
||||||
|
|
||||||
|
extraSpecs, err := flavors.ListExtraSpecs(computeClient, flavorID).Extract()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%+v", extraSpecs)
|
||||||
|
|
||||||
|
Example to Update Extra Specs for a Flavor
|
||||||
|
|
||||||
|
flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b"
|
||||||
|
|
||||||
|
updateOpts := flavors.ExtraSpecsOpts{
|
||||||
|
"hw:cpu_thread_policy": "CPU-THREAD-POLICY-UPDATED",
|
||||||
|
}
|
||||||
|
updatedExtraSpec, err := flavors.UpdateExtraSpec(computeClient, flavorID, updateOpts).Extract()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%+v", updatedExtraSpec)
|
||||||
|
|
||||||
|
Example to Delete an Extra Spec for a Flavor
|
||||||
|
|
||||||
|
flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b"
|
||||||
|
err := flavors.DeleteExtraSpec(computeClient, flavorID, "hw:cpu_thread_policy").ExtractErr()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
package flavors
|
||||||
347
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/requests.go
generated
vendored
Normal file
347
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/requests.go
generated
vendored
Normal file
|
|
@ -0,0 +1,347 @@
|
||||||
|
package flavors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListOptsBuilder allows extensions to add additional parameters to the
|
||||||
|
// List request.
|
||||||
|
type ListOptsBuilder interface {
|
||||||
|
ToFlavorListQuery() (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
AccessType maps to OpenStack's Flavor.is_public field. Although the is_public
|
||||||
|
field is boolean, the request options are ternary, which is why AccessType is
|
||||||
|
a string. The following values are allowed:
|
||||||
|
|
||||||
|
The AccessType arguement is optional, and if it is not supplied, OpenStack
|
||||||
|
returns the PublicAccess flavors.
|
||||||
|
*/
|
||||||
|
type AccessType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// PublicAccess returns public flavors and private flavors associated with
|
||||||
|
// that project.
|
||||||
|
PublicAccess AccessType = "true"
|
||||||
|
|
||||||
|
// PrivateAccess (admin only) returns private flavors, across all projects.
|
||||||
|
PrivateAccess AccessType = "false"
|
||||||
|
|
||||||
|
// AllAccess (admin only) returns public and private flavors across all
|
||||||
|
// projects.
|
||||||
|
AllAccess AccessType = "None"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
ListOpts filters the results returned by the List() function.
|
||||||
|
For example, a flavor with a minDisk field of 10 will not be returned if you
|
||||||
|
specify MinDisk set to 20.
|
||||||
|
|
||||||
|
Typically, software will use the last ID of the previous call to List to set
|
||||||
|
the Marker for the current call.
|
||||||
|
*/
|
||||||
|
type ListOpts struct {
|
||||||
|
// ChangesSince, if provided, instructs List to return only those things which
|
||||||
|
// have changed since the timestamp provided.
|
||||||
|
ChangesSince string `q:"changes-since"`
|
||||||
|
|
||||||
|
// MinDisk and MinRAM, if provided, elides flavors which do not meet your
|
||||||
|
// criteria.
|
||||||
|
MinDisk int `q:"minDisk"`
|
||||||
|
MinRAM int `q:"minRam"`
|
||||||
|
|
||||||
|
// Marker and Limit control paging.
|
||||||
|
// Marker instructs List where to start listing from.
|
||||||
|
Marker string `q:"marker"`
|
||||||
|
|
||||||
|
// Limit instructs List to refrain from sending excessively large lists of
|
||||||
|
// flavors.
|
||||||
|
Limit int `q:"limit"`
|
||||||
|
|
||||||
|
// AccessType, if provided, instructs List which set of flavors to return.
|
||||||
|
// If IsPublic not provided, flavors for the current project are returned.
|
||||||
|
AccessType AccessType `q:"is_public"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToFlavorListQuery formats a ListOpts into a query string.
|
||||||
|
func (opts ListOpts) ToFlavorListQuery() (string, error) {
|
||||||
|
q, err := gophercloud.BuildQueryString(opts)
|
||||||
|
return q.String(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListDetail instructs OpenStack to provide a list of flavors.
|
||||||
|
// You may provide criteria by which List curtails its results for easier
|
||||||
|
// processing.
|
||||||
|
func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
|
||||||
|
url := listURL(client)
|
||||||
|
if opts != nil {
|
||||||
|
query, err := opts.ToFlavorListQuery()
|
||||||
|
if err != nil {
|
||||||
|
return pagination.Pager{Err: err}
|
||||||
|
}
|
||||||
|
url += query
|
||||||
|
}
|
||||||
|
return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
|
||||||
|
return FlavorPage{pagination.LinkedPageBase{PageResult: r}}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateOptsBuilder interface {
|
||||||
|
ToFlavorCreateMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOpts specifies parameters used for creating a flavor.
|
||||||
|
type CreateOpts struct {
|
||||||
|
// Name is the name of the flavor.
|
||||||
|
Name string `json:"name" required:"true"`
|
||||||
|
|
||||||
|
// RAM is the memory of the flavor, measured in MB.
|
||||||
|
RAM int `json:"ram" required:"true"`
|
||||||
|
|
||||||
|
// VCPUs is the number of vcpus for the flavor.
|
||||||
|
VCPUs int `json:"vcpus" required:"true"`
|
||||||
|
|
||||||
|
// Disk the amount of root disk space, measured in GB.
|
||||||
|
Disk *int `json:"disk" required:"true"`
|
||||||
|
|
||||||
|
// ID is a unique ID for the flavor.
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
|
||||||
|
// Swap is the amount of swap space for the flavor, measured in MB.
|
||||||
|
Swap *int `json:"swap,omitempty"`
|
||||||
|
|
||||||
|
// RxTxFactor alters the network bandwidth of a flavor.
|
||||||
|
RxTxFactor float64 `json:"rxtx_factor,omitempty"`
|
||||||
|
|
||||||
|
// IsPublic flags a flavor as being available to all projects or not.
|
||||||
|
IsPublic *bool `json:"os-flavor-access:is_public,omitempty"`
|
||||||
|
|
||||||
|
// Ephemeral is the amount of ephemeral disk space, measured in GB.
|
||||||
|
Ephemeral *int `json:"OS-FLV-EXT-DATA:ephemeral,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToFlavorCreateMap constructs a request body from CreateOpts.
|
||||||
|
func (opts CreateOpts) ToFlavorCreateMap() (map[string]interface{}, error) {
|
||||||
|
return gophercloud.BuildRequestBody(opts, "flavor")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create requests the creation of a new flavor.
|
||||||
|
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
|
||||||
|
b, err := opts.ToFlavorCreateMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200, 201},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get retrieves details of a single flavor. Use ExtractFlavor to convert its
|
||||||
|
// result into a Flavor.
|
||||||
|
func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
|
||||||
|
_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete deletes the specified flavor ID.
|
||||||
|
func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
|
||||||
|
_, r.Err = client.Delete(deleteURL(client, id), nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListAccesses retrieves the tenants which have access to a flavor.
|
||||||
|
func ListAccesses(client *gophercloud.ServiceClient, id string) pagination.Pager {
|
||||||
|
url := accessURL(client, id)
|
||||||
|
|
||||||
|
return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
|
||||||
|
return AccessPage{pagination.SinglePageBase(r)}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAccessOptsBuilder allows extensions to add additional parameters to the
|
||||||
|
// AddAccess requests.
|
||||||
|
type AddAccessOptsBuilder interface {
|
||||||
|
ToFlavorAddAccessMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAccessOpts represents options for adding access to a flavor.
|
||||||
|
type AddAccessOpts struct {
|
||||||
|
// Tenant is the project/tenant ID to grant access.
|
||||||
|
Tenant string `json:"tenant"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToFlavorAddAccessMap constructs a request body from AddAccessOpts.
|
||||||
|
func (opts AddAccessOpts) ToFlavorAddAccessMap() (map[string]interface{}, error) {
|
||||||
|
return gophercloud.BuildRequestBody(opts, "addTenantAccess")
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAccess grants a tenant/project access to a flavor.
|
||||||
|
func AddAccess(client *gophercloud.ServiceClient, id string, opts AddAccessOptsBuilder) (r AddAccessResult) {
|
||||||
|
b, err := opts.ToFlavorAddAccessMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, r.Err = client.Post(accessActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveAccessOptsBuilder allows extensions to add additional parameters to the
|
||||||
|
// RemoveAccess requests.
|
||||||
|
type RemoveAccessOptsBuilder interface {
|
||||||
|
ToFlavorRemoveAccessMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveAccessOpts represents options for removing access to a flavor.
|
||||||
|
type RemoveAccessOpts struct {
|
||||||
|
// Tenant is the project/tenant ID to grant access.
|
||||||
|
Tenant string `json:"tenant"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToFlavorRemoveAccessMap constructs a request body from RemoveAccessOpts.
|
||||||
|
func (opts RemoveAccessOpts) ToFlavorRemoveAccessMap() (map[string]interface{}, error) {
|
||||||
|
return gophercloud.BuildRequestBody(opts, "removeTenantAccess")
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveAccess removes/revokes a tenant/project access to a flavor.
|
||||||
|
func RemoveAccess(client *gophercloud.ServiceClient, id string, opts RemoveAccessOptsBuilder) (r RemoveAccessResult) {
|
||||||
|
b, err := opts.ToFlavorRemoveAccessMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, r.Err = client.Post(accessActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtraSpecs requests all the extra-specs for the given flavor ID.
|
||||||
|
func ListExtraSpecs(client *gophercloud.ServiceClient, flavorID string) (r ListExtraSpecsResult) {
|
||||||
|
_, r.Err = client.Get(extraSpecsListURL(client, flavorID), &r.Body, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetExtraSpec(client *gophercloud.ServiceClient, flavorID string, key string) (r GetExtraSpecResult) {
|
||||||
|
_, r.Err = client.Get(extraSpecsGetURL(client, flavorID, key), &r.Body, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateExtraSpecsOptsBuilder allows extensions to add additional parameters to the
|
||||||
|
// CreateExtraSpecs requests.
|
||||||
|
type CreateExtraSpecsOptsBuilder interface {
|
||||||
|
ToExtraSpecsCreateMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtraSpecsOpts is a map that contains key-value pairs.
|
||||||
|
type ExtraSpecsOpts map[string]string
|
||||||
|
|
||||||
|
// ToExtraSpecsCreateMap assembles a body for a Create request based on the
|
||||||
|
// contents of a ExtraSpecsOpts
|
||||||
|
func (opts ExtraSpecsOpts) ToExtraSpecsCreateMap() (map[string]interface{}, error) {
|
||||||
|
return map[string]interface{}{"extra_specs": opts}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateExtraSpecs will create or update the extra-specs key-value pairs for the specified Flavor
|
||||||
|
func CreateExtraSpecs(client *gophercloud.ServiceClient, flavorID string, opts CreateExtraSpecsOptsBuilder) (r CreateExtraSpecsResult) {
|
||||||
|
b, err := opts.ToExtraSpecsCreateMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, r.Err = client.Post(extraSpecsCreateURL(client, flavorID), b, &r.Body, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateExtraSpecOptsBuilder allows extensions to add additional parameters to the
|
||||||
|
// Update request.
|
||||||
|
type UpdateExtraSpecOptsBuilder interface {
|
||||||
|
ToExtraSpecUpdateMap() (map[string]string, string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToExtraSpecUpdateMap assembles a body for an Update request based on the
|
||||||
|
// contents of a ExtraSpecOpts.
|
||||||
|
func (opts ExtraSpecsOpts) ToExtraSpecUpdateMap() (map[string]string, string, error) {
|
||||||
|
if len(opts) != 1 {
|
||||||
|
err := gophercloud.ErrInvalidInput{}
|
||||||
|
err.Argument = "flavors.ExtraSpecOpts"
|
||||||
|
err.Info = "Must have 1 and only one key-value pair"
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var key string
|
||||||
|
for k := range opts {
|
||||||
|
key = k
|
||||||
|
}
|
||||||
|
|
||||||
|
return opts, key, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateExtraSpec will updates the value of the specified flavor's extra spec for the key in opts.
|
||||||
|
func UpdateExtraSpec(client *gophercloud.ServiceClient, flavorID string, opts UpdateExtraSpecOptsBuilder) (r UpdateExtraSpecResult) {
|
||||||
|
b, key, err := opts.ToExtraSpecUpdateMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, r.Err = client.Put(extraSpecUpdateURL(client, flavorID, key), b, &r.Body, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteExtraSpec will delete the key-value pair with the given key for the given
|
||||||
|
// flavor ID.
|
||||||
|
func DeleteExtraSpec(client *gophercloud.ServiceClient, flavorID, key string) (r DeleteExtraSpecResult) {
|
||||||
|
_, r.Err = client.Delete(extraSpecDeleteURL(client, flavorID, key), &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDFromName is a convienience function that returns a flavor's ID given its
|
||||||
|
// name.
|
||||||
|
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
|
||||||
|
count := 0
|
||||||
|
id := ""
|
||||||
|
allPages, err := ListDetail(client, nil).AllPages()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
all, err := ExtractFlavors(allPages)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range all {
|
||||||
|
if f.Name == name {
|
||||||
|
count++
|
||||||
|
id = f.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch count {
|
||||||
|
case 0:
|
||||||
|
err := &gophercloud.ErrResourceNotFound{}
|
||||||
|
err.ResourceType = "flavor"
|
||||||
|
err.Name = name
|
||||||
|
return "", err
|
||||||
|
case 1:
|
||||||
|
return id, nil
|
||||||
|
default:
|
||||||
|
err := &gophercloud.ErrMultipleResourcesFound{}
|
||||||
|
err.ResourceType = "flavor"
|
||||||
|
err.Name = name
|
||||||
|
err.Count = count
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
252
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/results.go
generated
vendored
Normal file
252
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/results.go
generated
vendored
Normal file
|
|
@ -0,0 +1,252 @@
|
||||||
|
package flavors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
type commonResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateResult is the response of a Get operations. Call its Extract method to
|
||||||
|
// interpret it as a Flavor.
|
||||||
|
type CreateResult struct {
|
||||||
|
commonResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetResult is the response of a Get operations. Call its Extract method to
|
||||||
|
// interpret it as a Flavor.
|
||||||
|
type GetResult struct {
|
||||||
|
commonResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteResult is the result from a Delete operation. Call its ExtractErr
|
||||||
|
// method to determine if the call succeeded or failed.
|
||||||
|
type DeleteResult struct {
|
||||||
|
gophercloud.ErrResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract provides access to the individual Flavor returned by the Get and
|
||||||
|
// Create functions.
|
||||||
|
func (r commonResult) Extract() (*Flavor, error) {
|
||||||
|
var s struct {
|
||||||
|
Flavor *Flavor `json:"flavor"`
|
||||||
|
}
|
||||||
|
err := r.ExtractInto(&s)
|
||||||
|
return s.Flavor, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flavor represent (virtual) hardware configurations for server resources
|
||||||
|
// in a region.
|
||||||
|
type Flavor struct {
|
||||||
|
// ID is the flavor's unique ID.
|
||||||
|
ID string `json:"id"`
|
||||||
|
|
||||||
|
// Disk is the amount of root disk, measured in GB.
|
||||||
|
Disk int `json:"disk"`
|
||||||
|
|
||||||
|
// RAM is the amount of memory, measured in MB.
|
||||||
|
RAM int `json:"ram"`
|
||||||
|
|
||||||
|
// Name is the name of the flavor.
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// RxTxFactor describes bandwidth alterations of the flavor.
|
||||||
|
RxTxFactor float64 `json:"rxtx_factor"`
|
||||||
|
|
||||||
|
// Swap is the amount of swap space, measured in MB.
|
||||||
|
Swap int `json:"swap"`
|
||||||
|
|
||||||
|
// VCPUs indicates how many (virtual) CPUs are available for this flavor.
|
||||||
|
VCPUs int `json:"vcpus"`
|
||||||
|
|
||||||
|
// IsPublic indicates whether the flavor is public.
|
||||||
|
IsPublic bool `json:"os-flavor-access:is_public"`
|
||||||
|
|
||||||
|
// Ephemeral is the amount of ephemeral disk space, measured in GB.
|
||||||
|
Ephemeral int `json:"OS-FLV-EXT-DATA:ephemeral"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Flavor) UnmarshalJSON(b []byte) error {
|
||||||
|
type tmp Flavor
|
||||||
|
var s struct {
|
||||||
|
tmp
|
||||||
|
Swap interface{} `json:"swap"`
|
||||||
|
}
|
||||||
|
err := json.Unmarshal(b, &s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*r = Flavor(s.tmp)
|
||||||
|
|
||||||
|
switch t := s.Swap.(type) {
|
||||||
|
case float64:
|
||||||
|
r.Swap = int(t)
|
||||||
|
case string:
|
||||||
|
switch t {
|
||||||
|
case "":
|
||||||
|
r.Swap = 0
|
||||||
|
default:
|
||||||
|
swap, err := strconv.ParseFloat(t, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r.Swap = int(swap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlavorPage contains a single page of all flavors from a ListDetails call.
|
||||||
|
type FlavorPage struct {
|
||||||
|
pagination.LinkedPageBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty determines if a FlavorPage contains any results.
|
||||||
|
func (page FlavorPage) IsEmpty() (bool, error) {
|
||||||
|
flavors, err := ExtractFlavors(page)
|
||||||
|
return len(flavors) == 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextPageURL uses the response's embedded link reference to navigate to the
|
||||||
|
// next page of results.
|
||||||
|
func (page FlavorPage) NextPageURL() (string, error) {
|
||||||
|
var s struct {
|
||||||
|
Links []gophercloud.Link `json:"flavors_links"`
|
||||||
|
}
|
||||||
|
err := page.ExtractInto(&s)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return gophercloud.ExtractNextURL(s.Links)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractFlavors provides access to the list of flavors in a page acquired
|
||||||
|
// from the ListDetail operation.
|
||||||
|
func ExtractFlavors(r pagination.Page) ([]Flavor, error) {
|
||||||
|
var s struct {
|
||||||
|
Flavors []Flavor `json:"flavors"`
|
||||||
|
}
|
||||||
|
err := (r.(FlavorPage)).ExtractInto(&s)
|
||||||
|
return s.Flavors, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccessPage contains a single page of all FlavorAccess entries for a flavor.
|
||||||
|
type AccessPage struct {
|
||||||
|
pagination.SinglePageBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty indicates whether an AccessPage is empty.
|
||||||
|
func (page AccessPage) IsEmpty() (bool, error) {
|
||||||
|
v, err := ExtractAccesses(page)
|
||||||
|
return len(v) == 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractAccesses interprets a page of results as a slice of FlavorAccess.
|
||||||
|
func ExtractAccesses(r pagination.Page) ([]FlavorAccess, error) {
|
||||||
|
var s struct {
|
||||||
|
FlavorAccesses []FlavorAccess `json:"flavor_access"`
|
||||||
|
}
|
||||||
|
err := (r.(AccessPage)).ExtractInto(&s)
|
||||||
|
return s.FlavorAccesses, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type accessResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAccessResult is the response of an AddAccess operation. Call its
|
||||||
|
// Extract method to interpret it as a slice of FlavorAccess.
|
||||||
|
type AddAccessResult struct {
|
||||||
|
accessResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveAccessResult is the response of a RemoveAccess operation. Call its
|
||||||
|
// Extract method to interpret it as a slice of FlavorAccess.
|
||||||
|
type RemoveAccessResult struct {
|
||||||
|
accessResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract provides access to the result of an access create or delete.
|
||||||
|
// The result will be all accesses that the flavor has.
|
||||||
|
func (r accessResult) Extract() ([]FlavorAccess, error) {
|
||||||
|
var s struct {
|
||||||
|
FlavorAccesses []FlavorAccess `json:"flavor_access"`
|
||||||
|
}
|
||||||
|
err := r.ExtractInto(&s)
|
||||||
|
return s.FlavorAccesses, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlavorAccess represents an ACL of tenant access to a specific Flavor.
|
||||||
|
type FlavorAccess struct {
|
||||||
|
// FlavorID is the unique ID of the flavor.
|
||||||
|
FlavorID string `json:"flavor_id"`
|
||||||
|
|
||||||
|
// TenantID is the unique ID of the tenant.
|
||||||
|
TenantID string `json:"tenant_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract interprets any extraSpecsResult as ExtraSpecs, if possible.
|
||||||
|
func (r extraSpecsResult) Extract() (map[string]string, error) {
|
||||||
|
var s struct {
|
||||||
|
ExtraSpecs map[string]string `json:"extra_specs"`
|
||||||
|
}
|
||||||
|
err := r.ExtractInto(&s)
|
||||||
|
return s.ExtraSpecs, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// extraSpecsResult contains the result of a call for (potentially) multiple
|
||||||
|
// key-value pairs. Call its Extract method to interpret it as a
|
||||||
|
// map[string]interface.
|
||||||
|
type extraSpecsResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListExtraSpecsResult contains the result of a Get operation. Call its Extract
|
||||||
|
// method to interpret it as a map[string]interface.
|
||||||
|
type ListExtraSpecsResult struct {
|
||||||
|
extraSpecsResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateExtraSpecResult contains the result of a Create operation. Call its
|
||||||
|
// Extract method to interpret it as a map[string]interface.
|
||||||
|
type CreateExtraSpecsResult struct {
|
||||||
|
extraSpecsResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// extraSpecResult contains the result of a call for individual a single
|
||||||
|
// key-value pair.
|
||||||
|
type extraSpecResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetExtraSpecResult contains the result of a Get operation. Call its Extract
|
||||||
|
// method to interpret it as a map[string]interface.
|
||||||
|
type GetExtraSpecResult struct {
|
||||||
|
extraSpecResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateExtraSpecResult contains the result of an Update operation. Call its
|
||||||
|
// Extract method to interpret it as a map[string]interface.
|
||||||
|
type UpdateExtraSpecResult struct {
|
||||||
|
extraSpecResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteExtraSpecResult contains the result of a Delete operation. Call its
|
||||||
|
// ExtractErr method to determine if the call succeeded or failed.
|
||||||
|
type DeleteExtraSpecResult struct {
|
||||||
|
gophercloud.ErrResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract interprets any extraSpecResult as an ExtraSpec, if possible.
|
||||||
|
func (r extraSpecResult) Extract() (map[string]string, error) {
|
||||||
|
var s map[string]string
|
||||||
|
err := r.ExtractInto(&s)
|
||||||
|
return s, err
|
||||||
|
}
|
||||||
49
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/urls.go
generated
vendored
Normal file
49
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/urls.go
generated
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
package flavors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getURL(client *gophercloud.ServiceClient, id string) string {
|
||||||
|
return client.ServiceURL("flavors", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func listURL(client *gophercloud.ServiceClient) string {
|
||||||
|
return client.ServiceURL("flavors", "detail")
|
||||||
|
}
|
||||||
|
|
||||||
|
func createURL(client *gophercloud.ServiceClient) string {
|
||||||
|
return client.ServiceURL("flavors")
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteURL(client *gophercloud.ServiceClient, id string) string {
|
||||||
|
return client.ServiceURL("flavors", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func accessURL(client *gophercloud.ServiceClient, id string) string {
|
||||||
|
return client.ServiceURL("flavors", id, "os-flavor-access")
|
||||||
|
}
|
||||||
|
|
||||||
|
func accessActionURL(client *gophercloud.ServiceClient, id string) string {
|
||||||
|
return client.ServiceURL("flavors", id, "action")
|
||||||
|
}
|
||||||
|
|
||||||
|
func extraSpecsListURL(client *gophercloud.ServiceClient, id string) string {
|
||||||
|
return client.ServiceURL("flavors", id, "os-extra_specs")
|
||||||
|
}
|
||||||
|
|
||||||
|
func extraSpecsGetURL(client *gophercloud.ServiceClient, id, key string) string {
|
||||||
|
return client.ServiceURL("flavors", id, "os-extra_specs", key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func extraSpecsCreateURL(client *gophercloud.ServiceClient, id string) string {
|
||||||
|
return client.ServiceURL("flavors", id, "os-extra_specs")
|
||||||
|
}
|
||||||
|
|
||||||
|
func extraSpecUpdateURL(client *gophercloud.ServiceClient, id, key string) string {
|
||||||
|
return client.ServiceURL("flavors", id, "os-extra_specs", key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func extraSpecDeleteURL(client *gophercloud.ServiceClient, id, key string) string {
|
||||||
|
return client.ServiceURL("flavors", id, "os-extra_specs", key)
|
||||||
|
}
|
||||||
18
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/BUILD.bazel
generated
vendored
Normal file
18
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/BUILD.bazel
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = [
|
||||||
|
"doc.go",
|
||||||
|
"requests.go",
|
||||||
|
"results.go",
|
||||||
|
"urls.go",
|
||||||
|
],
|
||||||
|
importmap = "vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images",
|
||||||
|
importpath = "github.com/gophercloud/gophercloud/openstack/compute/v2/images",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
"//vendor/github.com/gophercloud/gophercloud:go_default_library",
|
||||||
|
"//vendor/github.com/gophercloud/gophercloud/pagination:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
||||||
32
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/doc.go
generated
vendored
Normal file
32
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
Package images provides information and interaction with the images through
|
||||||
|
the OpenStack Compute service.
|
||||||
|
|
||||||
|
This API is deprecated and will be removed from a future version of the Nova
|
||||||
|
API service.
|
||||||
|
|
||||||
|
An image is a collection of files used to create or rebuild a server.
|
||||||
|
Operators provide a number of pre-built OS images by default. You may also
|
||||||
|
create custom images from cloud servers you have launched.
|
||||||
|
|
||||||
|
Example to List Images
|
||||||
|
|
||||||
|
listOpts := images.ListOpts{
|
||||||
|
Limit: 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
allPages, err := images.ListDetail(computeClient, listOpts).AllPages()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
allImages, err := images.ExtractImages(allPages)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, image := range allImages {
|
||||||
|
fmt.Printf("%+v\n", image)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
package images
|
||||||
109
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/requests.go
generated
vendored
Normal file
109
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/requests.go
generated
vendored
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
package images
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListOptsBuilder allows extensions to add additional parameters to the
|
||||||
|
// ListDetail request.
|
||||||
|
type ListOptsBuilder interface {
|
||||||
|
ToImageListQuery() (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListOpts contain options filtering Images returned from a call to ListDetail.
|
||||||
|
type ListOpts struct {
|
||||||
|
// ChangesSince filters Images based on the last changed status (in date-time
|
||||||
|
// format).
|
||||||
|
ChangesSince string `q:"changes-since"`
|
||||||
|
|
||||||
|
// Limit limits the number of Images to return.
|
||||||
|
Limit int `q:"limit"`
|
||||||
|
|
||||||
|
// Mark is an Image UUID at which to set a marker.
|
||||||
|
Marker string `q:"marker"`
|
||||||
|
|
||||||
|
// Name is the name of the Image.
|
||||||
|
Name string `q:"name"`
|
||||||
|
|
||||||
|
// Server is the name of the Server (in URL format).
|
||||||
|
Server string `q:"server"`
|
||||||
|
|
||||||
|
// Status is the current status of the Image.
|
||||||
|
Status string `q:"status"`
|
||||||
|
|
||||||
|
// Type is the type of image (e.g. BASE, SERVER, ALL).
|
||||||
|
Type string `q:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToImageListQuery formats a ListOpts into a query string.
|
||||||
|
func (opts ListOpts) ToImageListQuery() (string, error) {
|
||||||
|
q, err := gophercloud.BuildQueryString(opts)
|
||||||
|
return q.String(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListDetail enumerates the available images.
|
||||||
|
func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
|
||||||
|
url := listDetailURL(client)
|
||||||
|
if opts != nil {
|
||||||
|
query, err := opts.ToImageListQuery()
|
||||||
|
if err != nil {
|
||||||
|
return pagination.Pager{Err: err}
|
||||||
|
}
|
||||||
|
url += query
|
||||||
|
}
|
||||||
|
return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
|
||||||
|
return ImagePage{pagination.LinkedPageBase{PageResult: r}}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns data about a specific image by its ID.
|
||||||
|
func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
|
||||||
|
_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete deletes the specified image ID.
|
||||||
|
func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
|
||||||
|
_, r.Err = client.Delete(deleteURL(client, id), nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDFromName is a convienience function that returns an image's ID given its
|
||||||
|
// name.
|
||||||
|
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
|
||||||
|
count := 0
|
||||||
|
id := ""
|
||||||
|
allPages, err := ListDetail(client, nil).AllPages()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
all, err := ExtractImages(allPages)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range all {
|
||||||
|
if f.Name == name {
|
||||||
|
count++
|
||||||
|
id = f.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch count {
|
||||||
|
case 0:
|
||||||
|
err := &gophercloud.ErrResourceNotFound{}
|
||||||
|
err.ResourceType = "image"
|
||||||
|
err.Name = name
|
||||||
|
return "", err
|
||||||
|
case 1:
|
||||||
|
return id, nil
|
||||||
|
default:
|
||||||
|
err := &gophercloud.ErrMultipleResourcesFound{}
|
||||||
|
err.ResourceType = "image"
|
||||||
|
err.Name = name
|
||||||
|
err.Count = count
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
95
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/results.go
generated
vendored
Normal file
95
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/results.go
generated
vendored
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
package images
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetResult is the response from a Get operation. Call its Extract method to
|
||||||
|
// interpret it as an Image.
|
||||||
|
type GetResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteResult is the result from a Delete operation. Call its ExtractErr
|
||||||
|
// method to determine if the call succeeded or failed.
|
||||||
|
type DeleteResult struct {
|
||||||
|
gophercloud.ErrResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract interprets a GetResult as an Image.
|
||||||
|
func (r GetResult) Extract() (*Image, error) {
|
||||||
|
var s struct {
|
||||||
|
Image *Image `json:"image"`
|
||||||
|
}
|
||||||
|
err := r.ExtractInto(&s)
|
||||||
|
return s.Image, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Image represents an Image returned by the Compute API.
|
||||||
|
type Image struct {
|
||||||
|
// ID is the unique ID of an image.
|
||||||
|
ID string
|
||||||
|
|
||||||
|
// Created is the date when the image was created.
|
||||||
|
Created string
|
||||||
|
|
||||||
|
// MinDisk is the minimum amount of disk a flavor must have to be able
|
||||||
|
// to create a server based on the image, measured in GB.
|
||||||
|
MinDisk int
|
||||||
|
|
||||||
|
// MinRAM is the minimum amount of RAM a flavor must have to be able
|
||||||
|
// to create a server based on the image, measured in MB.
|
||||||
|
MinRAM int
|
||||||
|
|
||||||
|
// Name provides a human-readable moniker for the OS image.
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// The Progress and Status fields indicate image-creation status.
|
||||||
|
Progress int
|
||||||
|
|
||||||
|
// Status is the current status of the image.
|
||||||
|
Status string
|
||||||
|
|
||||||
|
// Update is the date when the image was updated.
|
||||||
|
Updated string
|
||||||
|
|
||||||
|
// Metadata provides free-form key/value pairs that further describe the
|
||||||
|
// image.
|
||||||
|
Metadata map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImagePage contains a single page of all Images returne from a ListDetail
|
||||||
|
// operation. Use ExtractImages to convert it into a slice of usable structs.
|
||||||
|
type ImagePage struct {
|
||||||
|
pagination.LinkedPageBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty returns true if an ImagePage contains no Image results.
|
||||||
|
func (page ImagePage) IsEmpty() (bool, error) {
|
||||||
|
images, err := ExtractImages(page)
|
||||||
|
return len(images) == 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextPageURL uses the response's embedded link reference to navigate to the
|
||||||
|
// next page of results.
|
||||||
|
func (page ImagePage) NextPageURL() (string, error) {
|
||||||
|
var s struct {
|
||||||
|
Links []gophercloud.Link `json:"images_links"`
|
||||||
|
}
|
||||||
|
err := page.ExtractInto(&s)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return gophercloud.ExtractNextURL(s.Links)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractImages converts a page of List results into a slice of usable Image
|
||||||
|
// structs.
|
||||||
|
func ExtractImages(r pagination.Page) ([]Image, error) {
|
||||||
|
var s struct {
|
||||||
|
Images []Image `json:"images"`
|
||||||
|
}
|
||||||
|
err := (r.(ImagePage)).ExtractInto(&s)
|
||||||
|
return s.Images, err
|
||||||
|
}
|
||||||
15
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/urls.go
generated
vendored
Normal file
15
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/urls.go
generated
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
package images
|
||||||
|
|
||||||
|
import "github.com/gophercloud/gophercloud"
|
||||||
|
|
||||||
|
func listDetailURL(client *gophercloud.ServiceClient) string {
|
||||||
|
return client.ServiceURL("images", "detail")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getURL(client *gophercloud.ServiceClient, id string) string {
|
||||||
|
return client.ServiceURL("images", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteURL(client *gophercloud.ServiceClient, id string) string {
|
||||||
|
return client.ServiceURL("images", id)
|
||||||
|
}
|
||||||
22
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/BUILD.bazel
generated
vendored
Normal file
22
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/BUILD.bazel
generated
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = [
|
||||||
|
"doc.go",
|
||||||
|
"errors.go",
|
||||||
|
"requests.go",
|
||||||
|
"results.go",
|
||||||
|
"urls.go",
|
||||||
|
"util.go",
|
||||||
|
],
|
||||||
|
importmap = "vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers",
|
||||||
|
importpath = "github.com/gophercloud/gophercloud/openstack/compute/v2/servers",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
"//vendor/github.com/gophercloud/gophercloud:go_default_library",
|
||||||
|
"//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors:go_default_library",
|
||||||
|
"//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images:go_default_library",
|
||||||
|
"//vendor/github.com/gophercloud/gophercloud/pagination:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
||||||
115
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/doc.go
generated
vendored
Normal file
115
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
/*
|
||||||
|
Package servers provides information and interaction with the server API
|
||||||
|
resource in the OpenStack Compute service.
|
||||||
|
|
||||||
|
A server is a virtual machine instance in the compute system. In order for
|
||||||
|
one to be provisioned, a valid flavor and image are required.
|
||||||
|
|
||||||
|
Example to List Servers
|
||||||
|
|
||||||
|
listOpts := servers.ListOpts{
|
||||||
|
AllTenants: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
allPages, err := servers.List(computeClient, listOpts).AllPages()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
allServers, err := servers.ExtractServers(allPages)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, server := range allServers {
|
||||||
|
fmt.Printf("%+v\n", server)
|
||||||
|
}
|
||||||
|
|
||||||
|
Example to Create a Server
|
||||||
|
|
||||||
|
createOpts := servers.CreateOpts{
|
||||||
|
Name: "server_name",
|
||||||
|
ImageRef: "image-uuid",
|
||||||
|
FlavorRef: "flavor-uuid",
|
||||||
|
}
|
||||||
|
|
||||||
|
server, err := servers.Create(computeClient, createOpts).Extract()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
Example to Delete a Server
|
||||||
|
|
||||||
|
serverID := "d9072956-1560-487c-97f2-18bdf65ec749"
|
||||||
|
err := servers.Delete(computeClient, serverID).ExtractErr()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
Example to Force Delete a Server
|
||||||
|
|
||||||
|
serverID := "d9072956-1560-487c-97f2-18bdf65ec749"
|
||||||
|
err := servers.ForceDelete(computeClient, serverID).ExtractErr()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
Example to Reboot a Server
|
||||||
|
|
||||||
|
rebootOpts := servers.RebootOpts{
|
||||||
|
Type: servers.SoftReboot,
|
||||||
|
}
|
||||||
|
|
||||||
|
serverID := "d9072956-1560-487c-97f2-18bdf65ec749"
|
||||||
|
|
||||||
|
err := servers.Reboot(computeClient, serverID, rebootOpts).ExtractErr()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
Example to Rebuild a Server
|
||||||
|
|
||||||
|
rebuildOpts := servers.RebuildOpts{
|
||||||
|
Name: "new_name",
|
||||||
|
ImageID: "image-uuid",
|
||||||
|
}
|
||||||
|
|
||||||
|
serverID := "d9072956-1560-487c-97f2-18bdf65ec749"
|
||||||
|
|
||||||
|
server, err := servers.Rebuilt(computeClient, serverID, rebuildOpts).Extract()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
Example to Resize a Server
|
||||||
|
|
||||||
|
resizeOpts := servers.ResizeOpts{
|
||||||
|
FlavorRef: "flavor-uuid",
|
||||||
|
}
|
||||||
|
|
||||||
|
serverID := "d9072956-1560-487c-97f2-18bdf65ec749"
|
||||||
|
|
||||||
|
err := servers.Resize(computeClient, serverID, resizeOpts).ExtractErr()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = servers.ConfirmResize(computeClient, serverID).ExtractErr()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
Example to Snapshot a Server
|
||||||
|
|
||||||
|
snapshotOpts := servers.CreateImageOpts{
|
||||||
|
Name: "snapshot_name",
|
||||||
|
}
|
||||||
|
|
||||||
|
serverID := "d9072956-1560-487c-97f2-18bdf65ec749"
|
||||||
|
|
||||||
|
image, err := servers.CreateImage(computeClient, serverID, snapshotOpts).ExtractImageID()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
package servers
|
||||||
71
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/errors.go
generated
vendored
Normal file
71
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/errors.go
generated
vendored
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
package servers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrNeitherImageIDNorImageNameProvided is the error when neither the image
|
||||||
|
// ID nor the image name is provided for a server operation
|
||||||
|
type ErrNeitherImageIDNorImageNameProvided struct{ gophercloud.ErrMissingInput }
|
||||||
|
|
||||||
|
func (e ErrNeitherImageIDNorImageNameProvided) Error() string {
|
||||||
|
return "One and only one of the image ID and the image name must be provided."
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrNeitherFlavorIDNorFlavorNameProvided is the error when neither the flavor
|
||||||
|
// ID nor the flavor name is provided for a server operation
|
||||||
|
type ErrNeitherFlavorIDNorFlavorNameProvided struct{ gophercloud.ErrMissingInput }
|
||||||
|
|
||||||
|
func (e ErrNeitherFlavorIDNorFlavorNameProvided) Error() string {
|
||||||
|
return "One and only one of the flavor ID and the flavor name must be provided."
|
||||||
|
}
|
||||||
|
|
||||||
|
type ErrNoClientProvidedForIDByName struct{ gophercloud.ErrMissingInput }
|
||||||
|
|
||||||
|
func (e ErrNoClientProvidedForIDByName) Error() string {
|
||||||
|
return "A service client must be provided to find a resource ID by name."
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrInvalidHowParameterProvided is the error when an unknown value is given
|
||||||
|
// for the `how` argument
|
||||||
|
type ErrInvalidHowParameterProvided struct{ gophercloud.ErrInvalidInput }
|
||||||
|
|
||||||
|
// ErrNoAdminPassProvided is the error when an administrative password isn't
|
||||||
|
// provided for a server operation
|
||||||
|
type ErrNoAdminPassProvided struct{ gophercloud.ErrMissingInput }
|
||||||
|
|
||||||
|
// ErrNoImageIDProvided is the error when an image ID isn't provided for a server
|
||||||
|
// operation
|
||||||
|
type ErrNoImageIDProvided struct{ gophercloud.ErrMissingInput }
|
||||||
|
|
||||||
|
// ErrNoIDProvided is the error when a server ID isn't provided for a server
|
||||||
|
// operation
|
||||||
|
type ErrNoIDProvided struct{ gophercloud.ErrMissingInput }
|
||||||
|
|
||||||
|
// ErrServer is a generic error type for servers HTTP operations.
|
||||||
|
type ErrServer struct {
|
||||||
|
gophercloud.ErrUnexpectedResponseCode
|
||||||
|
ID string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (se ErrServer) Error() string {
|
||||||
|
return fmt.Sprintf("Error while executing HTTP request for server [%s]", se.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error404 overrides the generic 404 error message.
|
||||||
|
func (se ErrServer) Error404(e gophercloud.ErrUnexpectedResponseCode) error {
|
||||||
|
se.ErrUnexpectedResponseCode = e
|
||||||
|
return &ErrServerNotFound{se}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrServerNotFound is the error when a 404 is received during server HTTP
|
||||||
|
// operations.
|
||||||
|
type ErrServerNotFound struct {
|
||||||
|
ErrServer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrServerNotFound) Error() string {
|
||||||
|
return fmt.Sprintf("I couldn't find server [%s]", e.ID)
|
||||||
|
}
|
||||||
791
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/requests.go
generated
vendored
Normal file
791
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/requests.go
generated
vendored
Normal file
|
|
@ -0,0 +1,791 @@
|
||||||
|
package servers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/flavors"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/images"
|
||||||
|
"github.com/gophercloud/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListOptsBuilder allows extensions to add additional parameters to the
|
||||||
|
// List request.
|
||||||
|
type ListOptsBuilder interface {
|
||||||
|
ToServerListQuery() (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListOpts allows the filtering and sorting of paginated collections through
|
||||||
|
// the API. Filtering is achieved by passing in struct field values that map to
|
||||||
|
// the server attributes you want to see returned. Marker and Limit are used
|
||||||
|
// for pagination.
|
||||||
|
type ListOpts struct {
|
||||||
|
// ChangesSince is a time/date stamp for when the server last changed status.
|
||||||
|
ChangesSince string `q:"changes-since"`
|
||||||
|
|
||||||
|
// Image is the name of the image in URL format.
|
||||||
|
Image string `q:"image"`
|
||||||
|
|
||||||
|
// Flavor is the name of the flavor in URL format.
|
||||||
|
Flavor string `q:"flavor"`
|
||||||
|
|
||||||
|
// Name of the server as a string; can be queried with regular expressions.
|
||||||
|
// Realize that ?name=bob returns both bob and bobb. If you need to match bob
|
||||||
|
// only, you can use a regular expression matching the syntax of the
|
||||||
|
// underlying database server implemented for Compute.
|
||||||
|
Name string `q:"name"`
|
||||||
|
|
||||||
|
// Status is the value of the status of the server so that you can filter on
|
||||||
|
// "ACTIVE" for example.
|
||||||
|
Status string `q:"status"`
|
||||||
|
|
||||||
|
// Host is the name of the host as a string.
|
||||||
|
Host string `q:"host"`
|
||||||
|
|
||||||
|
// Marker is a UUID of the server at which you want to set a marker.
|
||||||
|
Marker string `q:"marker"`
|
||||||
|
|
||||||
|
// Limit is an integer value for the limit of values to return.
|
||||||
|
Limit int `q:"limit"`
|
||||||
|
|
||||||
|
// AllTenants is a bool to show all tenants.
|
||||||
|
AllTenants bool `q:"all_tenants"`
|
||||||
|
|
||||||
|
// TenantID lists servers for a particular tenant.
|
||||||
|
// Setting "AllTenants = true" is required.
|
||||||
|
TenantID string `q:"tenant_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToServerListQuery formats a ListOpts into a query string.
|
||||||
|
func (opts ListOpts) ToServerListQuery() (string, error) {
|
||||||
|
q, err := gophercloud.BuildQueryString(opts)
|
||||||
|
return q.String(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// List makes a request against the API to list servers accessible to you.
|
||||||
|
func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
|
||||||
|
url := listDetailURL(client)
|
||||||
|
if opts != nil {
|
||||||
|
query, err := opts.ToServerListQuery()
|
||||||
|
if err != nil {
|
||||||
|
return pagination.Pager{Err: err}
|
||||||
|
}
|
||||||
|
url += query
|
||||||
|
}
|
||||||
|
return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
|
||||||
|
return ServerPage{pagination.LinkedPageBase{PageResult: r}}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOptsBuilder allows extensions to add additional parameters to the
|
||||||
|
// Create request.
|
||||||
|
type CreateOptsBuilder interface {
|
||||||
|
ToServerCreateMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Network is used within CreateOpts to control a new server's network
|
||||||
|
// attachments.
|
||||||
|
type Network struct {
|
||||||
|
// UUID of a network to attach to the newly provisioned server.
|
||||||
|
// Required unless Port is provided.
|
||||||
|
UUID string
|
||||||
|
|
||||||
|
// Port of a neutron network to attach to the newly provisioned server.
|
||||||
|
// Required unless UUID is provided.
|
||||||
|
Port string
|
||||||
|
|
||||||
|
// FixedIP specifies a fixed IPv4 address to be used on this network.
|
||||||
|
FixedIP string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Personality is an array of files that are injected into the server at launch.
|
||||||
|
type Personality []*File
|
||||||
|
|
||||||
|
// File is used within CreateOpts and RebuildOpts to inject a file into the
|
||||||
|
// server at launch.
|
||||||
|
// File implements the json.Marshaler interface, so when a Create or Rebuild
|
||||||
|
// operation is requested, json.Marshal will call File's MarshalJSON method.
|
||||||
|
type File struct {
|
||||||
|
// Path of the file.
|
||||||
|
Path string
|
||||||
|
|
||||||
|
// Contents of the file. Maximum content size is 255 bytes.
|
||||||
|
Contents []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON marshals the escaped file, base64 encoding the contents.
|
||||||
|
func (f *File) MarshalJSON() ([]byte, error) {
|
||||||
|
file := struct {
|
||||||
|
Path string `json:"path"`
|
||||||
|
Contents string `json:"contents"`
|
||||||
|
}{
|
||||||
|
Path: f.Path,
|
||||||
|
Contents: base64.StdEncoding.EncodeToString(f.Contents),
|
||||||
|
}
|
||||||
|
return json.Marshal(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOpts specifies server creation parameters.
|
||||||
|
type CreateOpts struct {
|
||||||
|
// Name is the name to assign to the newly launched server.
|
||||||
|
Name string `json:"name" required:"true"`
|
||||||
|
|
||||||
|
// ImageRef [optional; required if ImageName is not provided] is the ID or
|
||||||
|
// full URL to the image that contains the server's OS and initial state.
|
||||||
|
// Also optional if using the boot-from-volume extension.
|
||||||
|
ImageRef string `json:"imageRef"`
|
||||||
|
|
||||||
|
// ImageName [optional; required if ImageRef is not provided] is the name of
|
||||||
|
// the image that contains the server's OS and initial state.
|
||||||
|
// Also optional if using the boot-from-volume extension.
|
||||||
|
ImageName string `json:"-"`
|
||||||
|
|
||||||
|
// FlavorRef [optional; required if FlavorName is not provided] is the ID or
|
||||||
|
// full URL to the flavor that describes the server's specs.
|
||||||
|
FlavorRef string `json:"flavorRef"`
|
||||||
|
|
||||||
|
// FlavorName [optional; required if FlavorRef is not provided] is the name of
|
||||||
|
// the flavor that describes the server's specs.
|
||||||
|
FlavorName string `json:"-"`
|
||||||
|
|
||||||
|
// SecurityGroups lists the names of the security groups to which this server
|
||||||
|
// should belong.
|
||||||
|
SecurityGroups []string `json:"-"`
|
||||||
|
|
||||||
|
// UserData contains configuration information or scripts to use upon launch.
|
||||||
|
// Create will base64-encode it for you, if it isn't already.
|
||||||
|
UserData []byte `json:"-"`
|
||||||
|
|
||||||
|
// AvailabilityZone in which to launch the server.
|
||||||
|
AvailabilityZone string `json:"availability_zone,omitempty"`
|
||||||
|
|
||||||
|
// Networks dictates how this server will be attached to available networks.
|
||||||
|
// By default, the server will be attached to all isolated networks for the
|
||||||
|
// tenant.
|
||||||
|
Networks []Network `json:"-"`
|
||||||
|
|
||||||
|
// Metadata contains key-value pairs (up to 255 bytes each) to attach to the
|
||||||
|
// server.
|
||||||
|
Metadata map[string]string `json:"metadata,omitempty"`
|
||||||
|
|
||||||
|
// Personality includes files to inject into the server at launch.
|
||||||
|
// Create will base64-encode file contents for you.
|
||||||
|
Personality Personality `json:"personality,omitempty"`
|
||||||
|
|
||||||
|
// ConfigDrive enables metadata injection through a configuration drive.
|
||||||
|
ConfigDrive *bool `json:"config_drive,omitempty"`
|
||||||
|
|
||||||
|
// AdminPass sets the root user password. If not set, a randomly-generated
|
||||||
|
// password will be created and returned in the response.
|
||||||
|
AdminPass string `json:"adminPass,omitempty"`
|
||||||
|
|
||||||
|
// AccessIPv4 specifies an IPv4 address for the instance.
|
||||||
|
AccessIPv4 string `json:"accessIPv4,omitempty"`
|
||||||
|
|
||||||
|
// AccessIPv6 pecifies an IPv6 address for the instance.
|
||||||
|
AccessIPv6 string `json:"accessIPv6,omitempty"`
|
||||||
|
|
||||||
|
// ServiceClient will allow calls to be made to retrieve an image or
|
||||||
|
// flavor ID by name.
|
||||||
|
ServiceClient *gophercloud.ServiceClient `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToServerCreateMap assembles a request body based on the contents of a
|
||||||
|
// CreateOpts.
|
||||||
|
func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) {
|
||||||
|
sc := opts.ServiceClient
|
||||||
|
opts.ServiceClient = nil
|
||||||
|
b, err := gophercloud.BuildRequestBody(opts, "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.UserData != nil {
|
||||||
|
var userData string
|
||||||
|
if _, err := base64.StdEncoding.DecodeString(string(opts.UserData)); err != nil {
|
||||||
|
userData = base64.StdEncoding.EncodeToString(opts.UserData)
|
||||||
|
} else {
|
||||||
|
userData = string(opts.UserData)
|
||||||
|
}
|
||||||
|
b["user_data"] = &userData
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(opts.SecurityGroups) > 0 {
|
||||||
|
securityGroups := make([]map[string]interface{}, len(opts.SecurityGroups))
|
||||||
|
for i, groupName := range opts.SecurityGroups {
|
||||||
|
securityGroups[i] = map[string]interface{}{"name": groupName}
|
||||||
|
}
|
||||||
|
b["security_groups"] = securityGroups
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(opts.Networks) > 0 {
|
||||||
|
networks := make([]map[string]interface{}, len(opts.Networks))
|
||||||
|
for i, net := range opts.Networks {
|
||||||
|
networks[i] = make(map[string]interface{})
|
||||||
|
if net.UUID != "" {
|
||||||
|
networks[i]["uuid"] = net.UUID
|
||||||
|
}
|
||||||
|
if net.Port != "" {
|
||||||
|
networks[i]["port"] = net.Port
|
||||||
|
}
|
||||||
|
if net.FixedIP != "" {
|
||||||
|
networks[i]["fixed_ip"] = net.FixedIP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b["networks"] = networks
|
||||||
|
}
|
||||||
|
|
||||||
|
// If ImageRef isn't provided, check if ImageName was provided to ascertain
|
||||||
|
// the image ID.
|
||||||
|
if opts.ImageRef == "" {
|
||||||
|
if opts.ImageName != "" {
|
||||||
|
if sc == nil {
|
||||||
|
err := ErrNoClientProvidedForIDByName{}
|
||||||
|
err.Argument = "ServiceClient"
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
imageID, err := images.IDFromName(sc, opts.ImageName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b["imageRef"] = imageID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If FlavorRef isn't provided, use FlavorName to ascertain the flavor ID.
|
||||||
|
if opts.FlavorRef == "" {
|
||||||
|
if opts.FlavorName == "" {
|
||||||
|
err := ErrNeitherFlavorIDNorFlavorNameProvided{}
|
||||||
|
err.Argument = "FlavorRef/FlavorName"
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if sc == nil {
|
||||||
|
err := ErrNoClientProvidedForIDByName{}
|
||||||
|
err.Argument = "ServiceClient"
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
flavorID, err := flavors.IDFromName(sc, opts.FlavorName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b["flavorRef"] = flavorID
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]interface{}{"server": b}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create requests a server to be provisioned to the user in the current tenant.
|
||||||
|
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
|
||||||
|
reqBody, err := opts.ToServerCreateMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, r.Err = client.Post(listURL(client), reqBody, &r.Body, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete requests that a server previously provisioned be removed from your
|
||||||
|
// account.
|
||||||
|
func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
|
||||||
|
_, r.Err = client.Delete(deleteURL(client, id), nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForceDelete forces the deletion of a server.
|
||||||
|
func ForceDelete(client *gophercloud.ServiceClient, id string) (r ActionResult) {
|
||||||
|
_, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"forceDelete": ""}, nil, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get requests details on a single server, by ID.
|
||||||
|
func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
|
||||||
|
_, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200, 203},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateOptsBuilder allows extensions to add additional attributes to the
|
||||||
|
// Update request.
|
||||||
|
type UpdateOptsBuilder interface {
|
||||||
|
ToServerUpdateMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateOpts specifies the base attributes that may be updated on an existing
|
||||||
|
// server.
|
||||||
|
type UpdateOpts struct {
|
||||||
|
// Name changes the displayed name of the server.
|
||||||
|
// The server host name will *not* change.
|
||||||
|
// Server names are not constrained to be unique, even within the same tenant.
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
|
||||||
|
// AccessIPv4 provides a new IPv4 address for the instance.
|
||||||
|
AccessIPv4 string `json:"accessIPv4,omitempty"`
|
||||||
|
|
||||||
|
// AccessIPv6 provides a new IPv6 address for the instance.
|
||||||
|
AccessIPv6 string `json:"accessIPv6,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToServerUpdateMap formats an UpdateOpts structure into a request body.
|
||||||
|
func (opts UpdateOpts) ToServerUpdateMap() (map[string]interface{}, error) {
|
||||||
|
return gophercloud.BuildRequestBody(opts, "server")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update requests that various attributes of the indicated server be changed.
|
||||||
|
func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
|
||||||
|
b, err := opts.ToServerUpdateMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChangeAdminPassword alters the administrator or root password for a specified
|
||||||
|
// server.
|
||||||
|
func ChangeAdminPassword(client *gophercloud.ServiceClient, id, newPassword string) (r ActionResult) {
|
||||||
|
b := map[string]interface{}{
|
||||||
|
"changePassword": map[string]string{
|
||||||
|
"adminPass": newPassword,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, r.Err = client.Post(actionURL(client, id), b, nil, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// RebootMethod describes the mechanisms by which a server reboot can be requested.
|
||||||
|
type RebootMethod string
|
||||||
|
|
||||||
|
// These constants determine how a server should be rebooted.
|
||||||
|
// See the Reboot() function for further details.
|
||||||
|
const (
|
||||||
|
SoftReboot RebootMethod = "SOFT"
|
||||||
|
HardReboot RebootMethod = "HARD"
|
||||||
|
OSReboot = SoftReboot
|
||||||
|
PowerCycle = HardReboot
|
||||||
|
)
|
||||||
|
|
||||||
|
// RebootOptsBuilder allows extensions to add additional parameters to the
|
||||||
|
// reboot request.
|
||||||
|
type RebootOptsBuilder interface {
|
||||||
|
ToServerRebootMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RebootOpts provides options to the reboot request.
|
||||||
|
type RebootOpts struct {
|
||||||
|
// Type is the type of reboot to perform on the server.
|
||||||
|
Type RebootMethod `json:"type" required:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToServerRebootMap builds a body for the reboot request.
|
||||||
|
func (opts *RebootOpts) ToServerRebootMap() (map[string]interface{}, error) {
|
||||||
|
return gophercloud.BuildRequestBody(opts, "reboot")
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Reboot requests that a given server reboot.
|
||||||
|
|
||||||
|
Two methods exist for rebooting a server:
|
||||||
|
|
||||||
|
HardReboot (aka PowerCycle) starts the server instance by physically cutting
|
||||||
|
power to the machine, or if a VM, terminating it at the hypervisor level.
|
||||||
|
It's done. Caput. Full stop.
|
||||||
|
Then, after a brief while, power is rtored or the VM instance restarted.
|
||||||
|
|
||||||
|
SoftReboot (aka OSReboot) simply tells the OS to restart under its own
|
||||||
|
procedure.
|
||||||
|
E.g., in Linux, asking it to enter runlevel 6, or executing
|
||||||
|
"sudo shutdown -r now", or by asking Windows to rtart the machine.
|
||||||
|
*/
|
||||||
|
func Reboot(client *gophercloud.ServiceClient, id string, opts RebootOptsBuilder) (r ActionResult) {
|
||||||
|
b, err := opts.ToServerRebootMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, r.Err = client.Post(actionURL(client, id), b, nil, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// RebuildOptsBuilder allows extensions to provide additional parameters to the
|
||||||
|
// rebuild request.
|
||||||
|
type RebuildOptsBuilder interface {
|
||||||
|
ToServerRebuildMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RebuildOpts represents the configuration options used in a server rebuild
|
||||||
|
// operation.
|
||||||
|
type RebuildOpts struct {
|
||||||
|
// AdminPass is the server's admin password
|
||||||
|
AdminPass string `json:"adminPass,omitempty"`
|
||||||
|
|
||||||
|
// ImageID is the ID of the image you want your server to be provisioned on.
|
||||||
|
ImageID string `json:"imageRef"`
|
||||||
|
|
||||||
|
// ImageName is readable name of an image.
|
||||||
|
ImageName string `json:"-"`
|
||||||
|
|
||||||
|
// Name to set the server to
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
|
||||||
|
// AccessIPv4 [optional] provides a new IPv4 address for the instance.
|
||||||
|
AccessIPv4 string `json:"accessIPv4,omitempty"`
|
||||||
|
|
||||||
|
// AccessIPv6 [optional] provides a new IPv6 address for the instance.
|
||||||
|
AccessIPv6 string `json:"accessIPv6,omitempty"`
|
||||||
|
|
||||||
|
// Metadata [optional] contains key-value pairs (up to 255 bytes each)
|
||||||
|
// to attach to the server.
|
||||||
|
Metadata map[string]string `json:"metadata,omitempty"`
|
||||||
|
|
||||||
|
// Personality [optional] includes files to inject into the server at launch.
|
||||||
|
// Rebuild will base64-encode file contents for you.
|
||||||
|
Personality Personality `json:"personality,omitempty"`
|
||||||
|
|
||||||
|
// ServiceClient will allow calls to be made to retrieve an image or
|
||||||
|
// flavor ID by name.
|
||||||
|
ServiceClient *gophercloud.ServiceClient `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToServerRebuildMap formats a RebuildOpts struct into a map for use in JSON
|
||||||
|
func (opts RebuildOpts) ToServerRebuildMap() (map[string]interface{}, error) {
|
||||||
|
b, err := gophercloud.BuildRequestBody(opts, "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If ImageRef isn't provided, check if ImageName was provided to ascertain
|
||||||
|
// the image ID.
|
||||||
|
if opts.ImageID == "" {
|
||||||
|
if opts.ImageName != "" {
|
||||||
|
if opts.ServiceClient == nil {
|
||||||
|
err := ErrNoClientProvidedForIDByName{}
|
||||||
|
err.Argument = "ServiceClient"
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
imageID, err := images.IDFromName(opts.ServiceClient, opts.ImageName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b["imageRef"] = imageID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]interface{}{"rebuild": b}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rebuild will reprovision the server according to the configuration options
|
||||||
|
// provided in the RebuildOpts struct.
|
||||||
|
func Rebuild(client *gophercloud.ServiceClient, id string, opts RebuildOptsBuilder) (r RebuildResult) {
|
||||||
|
b, err := opts.ToServerRebuildMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, r.Err = client.Post(actionURL(client, id), b, &r.Body, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResizeOptsBuilder allows extensions to add additional parameters to the
|
||||||
|
// resize request.
|
||||||
|
type ResizeOptsBuilder interface {
|
||||||
|
ToServerResizeMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResizeOpts represents the configuration options used to control a Resize
|
||||||
|
// operation.
|
||||||
|
type ResizeOpts struct {
|
||||||
|
// FlavorRef is the ID of the flavor you wish your server to become.
|
||||||
|
FlavorRef string `json:"flavorRef" required:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToServerResizeMap formats a ResizeOpts as a map that can be used as a JSON
|
||||||
|
// request body for the Resize request.
|
||||||
|
func (opts ResizeOpts) ToServerResizeMap() (map[string]interface{}, error) {
|
||||||
|
return gophercloud.BuildRequestBody(opts, "resize")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resize instructs the provider to change the flavor of the server.
|
||||||
|
//
|
||||||
|
// Note that this implies rebuilding it.
|
||||||
|
//
|
||||||
|
// Unfortunately, one cannot pass rebuild parameters to the resize function.
|
||||||
|
// When the resize completes, the server will be in VERIFY_RESIZE state.
|
||||||
|
// While in this state, you can explore the use of the new server's
|
||||||
|
// configuration. If you like it, call ConfirmResize() to commit the resize
|
||||||
|
// permanently. Otherwise, call RevertResize() to restore the old configuration.
|
||||||
|
func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder) (r ActionResult) {
|
||||||
|
b, err := opts.ToServerResizeMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, r.Err = client.Post(actionURL(client, id), b, nil, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfirmResize confirms a previous resize operation on a server.
|
||||||
|
// See Resize() for more details.
|
||||||
|
func ConfirmResize(client *gophercloud.ServiceClient, id string) (r ActionResult) {
|
||||||
|
_, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"confirmResize": nil}, nil, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{201, 202, 204},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// RevertResize cancels a previous resize operation on a server.
|
||||||
|
// See Resize() for more details.
|
||||||
|
func RevertResize(client *gophercloud.ServiceClient, id string) (r ActionResult) {
|
||||||
|
_, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"revertResize": nil}, nil, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// RescueOptsBuilder is an interface that allows extensions to override the
|
||||||
|
// default structure of a Rescue request.
|
||||||
|
type RescueOptsBuilder interface {
|
||||||
|
ToServerRescueMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RescueOpts represents the configuration options used to control a Rescue
|
||||||
|
// option.
|
||||||
|
type RescueOpts struct {
|
||||||
|
// AdminPass is the desired administrative password for the instance in
|
||||||
|
// RESCUE mode. If it's left blank, the server will generate a password.
|
||||||
|
AdminPass string `json:"adminPass,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToServerRescueMap formats a RescueOpts as a map that can be used as a JSON
|
||||||
|
// request body for the Rescue request.
|
||||||
|
func (opts RescueOpts) ToServerRescueMap() (map[string]interface{}, error) {
|
||||||
|
return gophercloud.BuildRequestBody(opts, "rescue")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rescue instructs the provider to place the server into RESCUE mode.
|
||||||
|
func Rescue(client *gophercloud.ServiceClient, id string, opts RescueOptsBuilder) (r RescueResult) {
|
||||||
|
b, err := opts.ToServerRescueMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetMetadataOptsBuilder allows extensions to add additional parameters to
|
||||||
|
// the Reset request.
|
||||||
|
type ResetMetadataOptsBuilder interface {
|
||||||
|
ToMetadataResetMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MetadataOpts is a map that contains key-value pairs.
|
||||||
|
type MetadataOpts map[string]string
|
||||||
|
|
||||||
|
// ToMetadataResetMap assembles a body for a Reset request based on the contents
|
||||||
|
// of a MetadataOpts.
|
||||||
|
func (opts MetadataOpts) ToMetadataResetMap() (map[string]interface{}, error) {
|
||||||
|
return map[string]interface{}{"metadata": opts}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToMetadataUpdateMap assembles a body for an Update request based on the
|
||||||
|
// contents of a MetadataOpts.
|
||||||
|
func (opts MetadataOpts) ToMetadataUpdateMap() (map[string]interface{}, error) {
|
||||||
|
return map[string]interface{}{"metadata": opts}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetMetadata will create multiple new key-value pairs for the given server
|
||||||
|
// ID.
|
||||||
|
// Note: Using this operation will erase any already-existing metadata and
|
||||||
|
// create the new metadata provided. To keep any already-existing metadata,
|
||||||
|
// use the UpdateMetadatas or UpdateMetadata function.
|
||||||
|
func ResetMetadata(client *gophercloud.ServiceClient, id string, opts ResetMetadataOptsBuilder) (r ResetMetadataResult) {
|
||||||
|
b, err := opts.ToMetadataResetMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, r.Err = client.Put(metadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metadata requests all the metadata for the given server ID.
|
||||||
|
func Metadata(client *gophercloud.ServiceClient, id string) (r GetMetadataResult) {
|
||||||
|
_, r.Err = client.Get(metadataURL(client, id), &r.Body, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateMetadataOptsBuilder allows extensions to add additional parameters to
|
||||||
|
// the Create request.
|
||||||
|
type UpdateMetadataOptsBuilder interface {
|
||||||
|
ToMetadataUpdateMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateMetadata updates (or creates) all the metadata specified by opts for
|
||||||
|
// the given server ID. This operation does not affect already-existing metadata
|
||||||
|
// that is not specified by opts.
|
||||||
|
func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) {
|
||||||
|
b, err := opts.ToMetadataUpdateMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, r.Err = client.Post(metadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// MetadatumOptsBuilder allows extensions to add additional parameters to the
|
||||||
|
// Create request.
|
||||||
|
type MetadatumOptsBuilder interface {
|
||||||
|
ToMetadatumCreateMap() (map[string]interface{}, string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MetadatumOpts is a map of length one that contains a key-value pair.
|
||||||
|
type MetadatumOpts map[string]string
|
||||||
|
|
||||||
|
// ToMetadatumCreateMap assembles a body for a Create request based on the
|
||||||
|
// contents of a MetadataumOpts.
|
||||||
|
func (opts MetadatumOpts) ToMetadatumCreateMap() (map[string]interface{}, string, error) {
|
||||||
|
if len(opts) != 1 {
|
||||||
|
err := gophercloud.ErrInvalidInput{}
|
||||||
|
err.Argument = "servers.MetadatumOpts"
|
||||||
|
err.Info = "Must have 1 and only 1 key-value pair"
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
metadatum := map[string]interface{}{"meta": opts}
|
||||||
|
var key string
|
||||||
|
for k := range metadatum["meta"].(MetadatumOpts) {
|
||||||
|
key = k
|
||||||
|
}
|
||||||
|
return metadatum, key, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateMetadatum will create or update the key-value pair with the given key
|
||||||
|
// for the given server ID.
|
||||||
|
func CreateMetadatum(client *gophercloud.ServiceClient, id string, opts MetadatumOptsBuilder) (r CreateMetadatumResult) {
|
||||||
|
b, key, err := opts.ToMetadatumCreateMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, r.Err = client.Put(metadatumURL(client, id, key), b, &r.Body, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metadatum requests the key-value pair with the given key for the given
|
||||||
|
// server ID.
|
||||||
|
func Metadatum(client *gophercloud.ServiceClient, id, key string) (r GetMetadatumResult) {
|
||||||
|
_, r.Err = client.Get(metadatumURL(client, id, key), &r.Body, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteMetadatum will delete the key-value pair with the given key for the
|
||||||
|
// given server ID.
|
||||||
|
func DeleteMetadatum(client *gophercloud.ServiceClient, id, key string) (r DeleteMetadatumResult) {
|
||||||
|
_, r.Err = client.Delete(metadatumURL(client, id, key), nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListAddresses makes a request against the API to list the servers IP
|
||||||
|
// addresses.
|
||||||
|
func ListAddresses(client *gophercloud.ServiceClient, id string) pagination.Pager {
|
||||||
|
return pagination.NewPager(client, listAddressesURL(client, id), func(r pagination.PageResult) pagination.Page {
|
||||||
|
return AddressPage{pagination.SinglePageBase(r)}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListAddressesByNetwork makes a request against the API to list the servers IP
|
||||||
|
// addresses for the given network.
|
||||||
|
func ListAddressesByNetwork(client *gophercloud.ServiceClient, id, network string) pagination.Pager {
|
||||||
|
return pagination.NewPager(client, listAddressesByNetworkURL(client, id, network), func(r pagination.PageResult) pagination.Page {
|
||||||
|
return NetworkAddressPage{pagination.SinglePageBase(r)}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateImageOptsBuilder allows extensions to add additional parameters to the
|
||||||
|
// CreateImage request.
|
||||||
|
type CreateImageOptsBuilder interface {
|
||||||
|
ToServerCreateImageMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateImageOpts provides options to pass to the CreateImage request.
|
||||||
|
type CreateImageOpts struct {
|
||||||
|
// Name of the image/snapshot.
|
||||||
|
Name string `json:"name" required:"true"`
|
||||||
|
|
||||||
|
// Metadata contains key-value pairs (up to 255 bytes each) to attach to
|
||||||
|
// the created image.
|
||||||
|
Metadata map[string]string `json:"metadata,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToServerCreateImageMap formats a CreateImageOpts structure into a request
|
||||||
|
// body.
|
||||||
|
func (opts CreateImageOpts) ToServerCreateImageMap() (map[string]interface{}, error) {
|
||||||
|
return gophercloud.BuildRequestBody(opts, "createImage")
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateImage makes a request against the nova API to schedule an image to be
|
||||||
|
// created of the server
|
||||||
|
func CreateImage(client *gophercloud.ServiceClient, id string, opts CreateImageOptsBuilder) (r CreateImageResult) {
|
||||||
|
b, err := opts.ToServerCreateImageMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{202},
|
||||||
|
})
|
||||||
|
r.Err = err
|
||||||
|
r.Header = resp.Header
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDFromName is a convienience function that returns a server's ID given its
|
||||||
|
// name.
|
||||||
|
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
|
||||||
|
count := 0
|
||||||
|
id := ""
|
||||||
|
allPages, err := List(client, nil).AllPages()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
all, err := ExtractServers(allPages)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range all {
|
||||||
|
if f.Name == name {
|
||||||
|
count++
|
||||||
|
id = f.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch count {
|
||||||
|
case 0:
|
||||||
|
return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "server"}
|
||||||
|
case 1:
|
||||||
|
return id, nil
|
||||||
|
default:
|
||||||
|
return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "server"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPassword makes a request against the nova API to get the encrypted
|
||||||
|
// administrative password.
|
||||||
|
func GetPassword(client *gophercloud.ServiceClient, serverId string) (r GetPasswordResult) {
|
||||||
|
_, r.Err = client.Get(passwordURL(client, serverId), &r.Body, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
414
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/results.go
generated
vendored
Normal file
414
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/results.go
generated
vendored
Normal file
|
|
@ -0,0 +1,414 @@
|
||||||
|
package servers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rsa"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"path"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
type serverResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract interprets any serverResult as a Server, if possible.
|
||||||
|
func (r serverResult) Extract() (*Server, error) {
|
||||||
|
var s Server
|
||||||
|
err := r.ExtractInto(&s)
|
||||||
|
return &s, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r serverResult) ExtractInto(v interface{}) error {
|
||||||
|
return r.Result.ExtractIntoStructPtr(v, "server")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExtractServersInto(r pagination.Page, v interface{}) error {
|
||||||
|
return r.(ServerPage).Result.ExtractIntoSlicePtr(v, "servers")
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateResult is the response from a Create operation. Call its Extract
|
||||||
|
// method to interpret it as a Server.
|
||||||
|
type CreateResult struct {
|
||||||
|
serverResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetResult is the response from a Get operation. Call its Extract
|
||||||
|
// method to interpret it as a Server.
|
||||||
|
type GetResult struct {
|
||||||
|
serverResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateResult is the response from an Update operation. Call its Extract
|
||||||
|
// method to interpret it as a Server.
|
||||||
|
type UpdateResult struct {
|
||||||
|
serverResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteResult is the response from a Delete operation. Call its ExtractErr
|
||||||
|
// method to determine if the call succeeded or failed.
|
||||||
|
type DeleteResult struct {
|
||||||
|
gophercloud.ErrResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// RebuildResult is the response from a Rebuild operation. Call its Extract
|
||||||
|
// method to interpret it as a Server.
|
||||||
|
type RebuildResult struct {
|
||||||
|
serverResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// ActionResult represents the result of server action operations, like reboot.
|
||||||
|
// Call its ExtractErr method to determine if the action succeeded or failed.
|
||||||
|
type ActionResult struct {
|
||||||
|
gophercloud.ErrResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// RescueResult is the response from a Rescue operation. Call its ExtractErr
|
||||||
|
// method to determine if the call succeeded or failed.
|
||||||
|
type RescueResult struct {
|
||||||
|
ActionResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateImageResult is the response from a CreateImage operation. Call its
|
||||||
|
// ExtractImageID method to retrieve the ID of the newly created image.
|
||||||
|
type CreateImageResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPasswordResult represent the result of a get os-server-password operation.
|
||||||
|
// Call its ExtractPassword method to retrieve the password.
|
||||||
|
type GetPasswordResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractPassword gets the encrypted password.
|
||||||
|
// If privateKey != nil the password is decrypted with the private key.
|
||||||
|
// If privateKey == nil the encrypted password is returned and can be decrypted
|
||||||
|
// with:
|
||||||
|
// echo '<pwd>' | base64 -D | openssl rsautl -decrypt -inkey <private_key>
|
||||||
|
func (r GetPasswordResult) ExtractPassword(privateKey *rsa.PrivateKey) (string, error) {
|
||||||
|
var s struct {
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
||||||
|
err := r.ExtractInto(&s)
|
||||||
|
if err == nil && privateKey != nil && s.Password != "" {
|
||||||
|
return decryptPassword(s.Password, privateKey)
|
||||||
|
}
|
||||||
|
return s.Password, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func decryptPassword(encryptedPassword string, privateKey *rsa.PrivateKey) (string, error) {
|
||||||
|
b64EncryptedPassword := make([]byte, base64.StdEncoding.DecodedLen(len(encryptedPassword)))
|
||||||
|
|
||||||
|
n, err := base64.StdEncoding.Decode(b64EncryptedPassword, []byte(encryptedPassword))
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Failed to base64 decode encrypted password: %s", err)
|
||||||
|
}
|
||||||
|
password, err := rsa.DecryptPKCS1v15(nil, privateKey, b64EncryptedPassword[0:n])
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Failed to decrypt password: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(password), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractImageID gets the ID of the newly created server image from the header.
|
||||||
|
func (r CreateImageResult) ExtractImageID() (string, error) {
|
||||||
|
if r.Err != nil {
|
||||||
|
return "", r.Err
|
||||||
|
}
|
||||||
|
// Get the image id from the header
|
||||||
|
u, err := url.ParseRequestURI(r.Header.Get("Location"))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
imageID := path.Base(u.Path)
|
||||||
|
if imageID == "." || imageID == "/" {
|
||||||
|
return "", fmt.Errorf("Failed to parse the ID of newly created image: %s", u)
|
||||||
|
}
|
||||||
|
return imageID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract interprets any RescueResult as an AdminPass, if possible.
|
||||||
|
func (r RescueResult) Extract() (string, error) {
|
||||||
|
var s struct {
|
||||||
|
AdminPass string `json:"adminPass"`
|
||||||
|
}
|
||||||
|
err := r.ExtractInto(&s)
|
||||||
|
return s.AdminPass, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server represents a server/instance in the OpenStack cloud.
|
||||||
|
type Server struct {
|
||||||
|
// ID uniquely identifies this server amongst all other servers,
|
||||||
|
// including those not accessible to the current tenant.
|
||||||
|
ID string `json:"id"`
|
||||||
|
|
||||||
|
// TenantID identifies the tenant owning this server resource.
|
||||||
|
TenantID string `json:"tenant_id"`
|
||||||
|
|
||||||
|
// UserID uniquely identifies the user account owning the tenant.
|
||||||
|
UserID string `json:"user_id"`
|
||||||
|
|
||||||
|
// Name contains the human-readable name for the server.
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// Updated and Created contain ISO-8601 timestamps of when the state of the
|
||||||
|
// server last changed, and when it was created.
|
||||||
|
Updated time.Time `json:"updated"`
|
||||||
|
Created time.Time `json:"created"`
|
||||||
|
|
||||||
|
// HostID is the host where the server is located in the cloud.
|
||||||
|
HostID string `json:"hostid"`
|
||||||
|
|
||||||
|
// Status contains the current operational status of the server,
|
||||||
|
// such as IN_PROGRESS or ACTIVE.
|
||||||
|
Status string `json:"status"`
|
||||||
|
|
||||||
|
// Progress ranges from 0..100.
|
||||||
|
// A request made against the server completes only once Progress reaches 100.
|
||||||
|
Progress int `json:"progress"`
|
||||||
|
|
||||||
|
// AccessIPv4 and AccessIPv6 contain the IP addresses of the server,
|
||||||
|
// suitable for remote access for administration.
|
||||||
|
AccessIPv4 string `json:"accessIPv4"`
|
||||||
|
AccessIPv6 string `json:"accessIPv6"`
|
||||||
|
|
||||||
|
// Image refers to a JSON object, which itself indicates the OS image used to
|
||||||
|
// deploy the server.
|
||||||
|
Image map[string]interface{} `json:"-"`
|
||||||
|
|
||||||
|
// Flavor refers to a JSON object, which itself indicates the hardware
|
||||||
|
// configuration of the deployed server.
|
||||||
|
Flavor map[string]interface{} `json:"flavor"`
|
||||||
|
|
||||||
|
// Addresses includes a list of all IP addresses assigned to the server,
|
||||||
|
// keyed by pool.
|
||||||
|
Addresses map[string]interface{} `json:"addresses"`
|
||||||
|
|
||||||
|
// Metadata includes a list of all user-specified key-value pairs attached
|
||||||
|
// to the server.
|
||||||
|
Metadata map[string]string `json:"metadata"`
|
||||||
|
|
||||||
|
// Links includes HTTP references to the itself, useful for passing along to
|
||||||
|
// other APIs that might want a server reference.
|
||||||
|
Links []interface{} `json:"links"`
|
||||||
|
|
||||||
|
// KeyName indicates which public key was injected into the server on launch.
|
||||||
|
KeyName string `json:"key_name"`
|
||||||
|
|
||||||
|
// AdminPass will generally be empty (""). However, it will contain the
|
||||||
|
// administrative password chosen when provisioning a new server without a
|
||||||
|
// set AdminPass setting in the first place.
|
||||||
|
// Note that this is the ONLY time this field will be valid.
|
||||||
|
AdminPass string `json:"adminPass"`
|
||||||
|
|
||||||
|
// SecurityGroups includes the security groups that this instance has applied
|
||||||
|
// to it.
|
||||||
|
SecurityGroups []map[string]interface{} `json:"security_groups"`
|
||||||
|
|
||||||
|
// Fault contains failure information about a server.
|
||||||
|
Fault Fault `json:"fault"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Fault struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Created time.Time `json:"created"`
|
||||||
|
Details string `json:"details"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Server) UnmarshalJSON(b []byte) error {
|
||||||
|
type tmp Server
|
||||||
|
var s struct {
|
||||||
|
tmp
|
||||||
|
Image interface{} `json:"image"`
|
||||||
|
}
|
||||||
|
err := json.Unmarshal(b, &s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*r = Server(s.tmp)
|
||||||
|
|
||||||
|
switch t := s.Image.(type) {
|
||||||
|
case map[string]interface{}:
|
||||||
|
r.Image = t
|
||||||
|
case string:
|
||||||
|
switch t {
|
||||||
|
case "":
|
||||||
|
r.Image = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerPage abstracts the raw results of making a List() request against
|
||||||
|
// the API. As OpenStack extensions may freely alter the response bodies of
|
||||||
|
// structures returned to the client, you may only safely access the data
|
||||||
|
// provided through the ExtractServers call.
|
||||||
|
type ServerPage struct {
|
||||||
|
pagination.LinkedPageBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty returns true if a page contains no Server results.
|
||||||
|
func (r ServerPage) IsEmpty() (bool, error) {
|
||||||
|
s, err := ExtractServers(r)
|
||||||
|
return len(s) == 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextPageURL uses the response's embedded link reference to navigate to the
|
||||||
|
// next page of results.
|
||||||
|
func (r ServerPage) NextPageURL() (string, error) {
|
||||||
|
var s struct {
|
||||||
|
Links []gophercloud.Link `json:"servers_links"`
|
||||||
|
}
|
||||||
|
err := r.ExtractInto(&s)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return gophercloud.ExtractNextURL(s.Links)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractServers interprets the results of a single page from a List() call,
|
||||||
|
// producing a slice of Server entities.
|
||||||
|
func ExtractServers(r pagination.Page) ([]Server, error) {
|
||||||
|
var s []Server
|
||||||
|
err := ExtractServersInto(r, &s)
|
||||||
|
return s, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// MetadataResult contains the result of a call for (potentially) multiple
|
||||||
|
// key-value pairs. Call its Extract method to interpret it as a
|
||||||
|
// map[string]interface.
|
||||||
|
type MetadataResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMetadataResult contains the result of a Get operation. Call its Extract
|
||||||
|
// method to interpret it as a map[string]interface.
|
||||||
|
type GetMetadataResult struct {
|
||||||
|
MetadataResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetMetadataResult contains the result of a Reset operation. Call its
|
||||||
|
// Extract method to interpret it as a map[string]interface.
|
||||||
|
type ResetMetadataResult struct {
|
||||||
|
MetadataResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateMetadataResult contains the result of an Update operation. Call its
|
||||||
|
// Extract method to interpret it as a map[string]interface.
|
||||||
|
type UpdateMetadataResult struct {
|
||||||
|
MetadataResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// MetadatumResult contains the result of a call for individual a single
|
||||||
|
// key-value pair.
|
||||||
|
type MetadatumResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMetadatumResult contains the result of a Get operation. Call its Extract
|
||||||
|
// method to interpret it as a map[string]interface.
|
||||||
|
type GetMetadatumResult struct {
|
||||||
|
MetadatumResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateMetadatumResult contains the result of a Create operation. Call its
|
||||||
|
// Extract method to interpret it as a map[string]interface.
|
||||||
|
type CreateMetadatumResult struct {
|
||||||
|
MetadatumResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteMetadatumResult contains the result of a Delete operation. Call its
|
||||||
|
// ExtractErr method to determine if the call succeeded or failed.
|
||||||
|
type DeleteMetadatumResult struct {
|
||||||
|
gophercloud.ErrResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract interprets any MetadataResult as a Metadata, if possible.
|
||||||
|
func (r MetadataResult) Extract() (map[string]string, error) {
|
||||||
|
var s struct {
|
||||||
|
Metadata map[string]string `json:"metadata"`
|
||||||
|
}
|
||||||
|
err := r.ExtractInto(&s)
|
||||||
|
return s.Metadata, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract interprets any MetadatumResult as a Metadatum, if possible.
|
||||||
|
func (r MetadatumResult) Extract() (map[string]string, error) {
|
||||||
|
var s struct {
|
||||||
|
Metadatum map[string]string `json:"meta"`
|
||||||
|
}
|
||||||
|
err := r.ExtractInto(&s)
|
||||||
|
return s.Metadatum, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Address represents an IP address.
|
||||||
|
type Address struct {
|
||||||
|
Version int `json:"version"`
|
||||||
|
Address string `json:"addr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddressPage abstracts the raw results of making a ListAddresses() request
|
||||||
|
// against the API. As OpenStack extensions may freely alter the response bodies
|
||||||
|
// of structures returned to the client, you may only safely access the data
|
||||||
|
// provided through the ExtractAddresses call.
|
||||||
|
type AddressPage struct {
|
||||||
|
pagination.SinglePageBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty returns true if an AddressPage contains no networks.
|
||||||
|
func (r AddressPage) IsEmpty() (bool, error) {
|
||||||
|
addresses, err := ExtractAddresses(r)
|
||||||
|
return len(addresses) == 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractAddresses interprets the results of a single page from a
|
||||||
|
// ListAddresses() call, producing a map of addresses.
|
||||||
|
func ExtractAddresses(r pagination.Page) (map[string][]Address, error) {
|
||||||
|
var s struct {
|
||||||
|
Addresses map[string][]Address `json:"addresses"`
|
||||||
|
}
|
||||||
|
err := (r.(AddressPage)).ExtractInto(&s)
|
||||||
|
return s.Addresses, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkAddressPage abstracts the raw results of making a
|
||||||
|
// ListAddressesByNetwork() request against the API.
|
||||||
|
// As OpenStack extensions may freely alter the response bodies of structures
|
||||||
|
// returned to the client, you may only safely access the data provided through
|
||||||
|
// the ExtractAddresses call.
|
||||||
|
type NetworkAddressPage struct {
|
||||||
|
pagination.SinglePageBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty returns true if a NetworkAddressPage contains no addresses.
|
||||||
|
func (r NetworkAddressPage) IsEmpty() (bool, error) {
|
||||||
|
addresses, err := ExtractNetworkAddresses(r)
|
||||||
|
return len(addresses) == 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractNetworkAddresses interprets the results of a single page from a
|
||||||
|
// ListAddressesByNetwork() call, producing a slice of addresses.
|
||||||
|
func ExtractNetworkAddresses(r pagination.Page) ([]Address, error) {
|
||||||
|
var s map[string][]Address
|
||||||
|
err := (r.(NetworkAddressPage)).ExtractInto(&s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var key string
|
||||||
|
for k := range s {
|
||||||
|
key = k
|
||||||
|
}
|
||||||
|
|
||||||
|
return s[key], err
|
||||||
|
}
|
||||||
51
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/urls.go
generated
vendored
Normal file
51
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/urls.go
generated
vendored
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
package servers
|
||||||
|
|
||||||
|
import "github.com/gophercloud/gophercloud"
|
||||||
|
|
||||||
|
func createURL(client *gophercloud.ServiceClient) string {
|
||||||
|
return client.ServiceURL("servers")
|
||||||
|
}
|
||||||
|
|
||||||
|
func listURL(client *gophercloud.ServiceClient) string {
|
||||||
|
return createURL(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
func listDetailURL(client *gophercloud.ServiceClient) string {
|
||||||
|
return client.ServiceURL("servers", "detail")
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteURL(client *gophercloud.ServiceClient, id string) string {
|
||||||
|
return client.ServiceURL("servers", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getURL(client *gophercloud.ServiceClient, id string) string {
|
||||||
|
return deleteURL(client, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateURL(client *gophercloud.ServiceClient, id string) string {
|
||||||
|
return deleteURL(client, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func actionURL(client *gophercloud.ServiceClient, id string) string {
|
||||||
|
return client.ServiceURL("servers", id, "action")
|
||||||
|
}
|
||||||
|
|
||||||
|
func metadatumURL(client *gophercloud.ServiceClient, id, key string) string {
|
||||||
|
return client.ServiceURL("servers", id, "metadata", key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func metadataURL(client *gophercloud.ServiceClient, id string) string {
|
||||||
|
return client.ServiceURL("servers", id, "metadata")
|
||||||
|
}
|
||||||
|
|
||||||
|
func listAddressesURL(client *gophercloud.ServiceClient, id string) string {
|
||||||
|
return client.ServiceURL("servers", id, "ips")
|
||||||
|
}
|
||||||
|
|
||||||
|
func listAddressesByNetworkURL(client *gophercloud.ServiceClient, id, network string) string {
|
||||||
|
return client.ServiceURL("servers", id, "ips", network)
|
||||||
|
}
|
||||||
|
|
||||||
|
func passwordURL(client *gophercloud.ServiceClient, id string) string {
|
||||||
|
return client.ServiceURL("servers", id, "os-server-password")
|
||||||
|
}
|
||||||
21
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/util.go
generated
vendored
Normal file
21
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/util.go
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
package servers
|
||||||
|
|
||||||
|
import "github.com/gophercloud/gophercloud"
|
||||||
|
|
||||||
|
// WaitForStatus will continually poll a server until it successfully
|
||||||
|
// transitions to a specified status. It will do this for at most the number
|
||||||
|
// of seconds specified.
|
||||||
|
func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error {
|
||||||
|
return gophercloud.WaitFor(secs, func() (bool, error) {
|
||||||
|
current, err := Get(c, id).Extract()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if current.Status == status {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue