Move deps from _workspace/ to vendor/
godep restore pushd $GOPATH/src/github.com/appc/spec git co master popd go get go4.org/errorutil rm -rf Godeps godep save ./... git add vendor git add -f $(git ls-files --other vendor/) git co -- Godeps/LICENSES Godeps/.license_file_state Godeps/OWNERS
This commit is contained in:
118
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume/requests.go
generated
vendored
Normal file
118
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume/requests.go
generated
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
package bootfromvolume
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
||||
)
|
||||
|
||||
// SourceType represents the type of medium being used to create the volume.
|
||||
type SourceType string
|
||||
|
||||
const (
|
||||
Volume SourceType = "volume"
|
||||
Snapshot SourceType = "snapshot"
|
||||
Image SourceType = "image"
|
||||
Blank SourceType = "blank"
|
||||
)
|
||||
|
||||
// BlockDevice is a structure with options for booting a server instance
|
||||
// from a volume. The volume may be created from an image, snapshot, or another
|
||||
// volume.
|
||||
type BlockDevice struct {
|
||||
// BootIndex [optional] is the boot index. It defaults to 0.
|
||||
BootIndex int `json:"boot_index"`
|
||||
|
||||
// DeleteOnTermination [optional] specifies whether or not to delete the attached volume
|
||||
// when the server is deleted. Defaults to `false`.
|
||||
DeleteOnTermination bool `json:"delete_on_termination"`
|
||||
|
||||
// DestinationType [optional] is the type that gets created. Possible values are "volume"
|
||||
// and "local".
|
||||
DestinationType string `json:"destination_type"`
|
||||
|
||||
// GuestFormat [optional] specifies the format of the block device.
|
||||
GuestFormat string `json:"guest_format"`
|
||||
|
||||
// SourceType [required] must be one of: "volume", "snapshot", "image".
|
||||
SourceType SourceType `json:"source_type"`
|
||||
|
||||
// UUID [required] is the unique identifier for the volume, snapshot, or image (see above)
|
||||
UUID string `json:"uuid"`
|
||||
|
||||
// VolumeSize [optional] is the size of the volume to create (in gigabytes).
|
||||
VolumeSize int `json:"volume_size"`
|
||||
}
|
||||
|
||||
// CreateOptsExt is a structure that extends the server `CreateOpts` structure
|
||||
// by allowing for a block device mapping.
|
||||
type CreateOptsExt struct {
|
||||
servers.CreateOptsBuilder
|
||||
BlockDevice []BlockDevice `json:"block_device_mapping_v2,omitempty"`
|
||||
}
|
||||
|
||||
// ToServerCreateMap adds the block device mapping option 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 len(opts.BlockDevice) == 0 {
|
||||
return nil, errors.New("Required fields UUID and SourceType not set.")
|
||||
}
|
||||
|
||||
serverMap := base["server"].(map[string]interface{})
|
||||
|
||||
blockDevice := make([]map[string]interface{}, len(opts.BlockDevice))
|
||||
|
||||
for i, bd := range opts.BlockDevice {
|
||||
if string(bd.SourceType) == "" {
|
||||
return nil, errors.New("SourceType must be one of: volume, image, snapshot.")
|
||||
}
|
||||
|
||||
blockDevice[i] = make(map[string]interface{})
|
||||
|
||||
blockDevice[i]["source_type"] = bd.SourceType
|
||||
blockDevice[i]["boot_index"] = strconv.Itoa(bd.BootIndex)
|
||||
blockDevice[i]["delete_on_termination"] = strconv.FormatBool(bd.DeleteOnTermination)
|
||||
blockDevice[i]["volume_size"] = strconv.Itoa(bd.VolumeSize)
|
||||
if bd.UUID != "" {
|
||||
blockDevice[i]["uuid"] = bd.UUID
|
||||
}
|
||||
if bd.DestinationType != "" {
|
||||
blockDevice[i]["destination_type"] = bd.DestinationType
|
||||
}
|
||||
if bd.GuestFormat != "" {
|
||||
blockDevice[i]["guest_format"] = bd.GuestFormat
|
||||
}
|
||||
|
||||
}
|
||||
serverMap["block_device_mapping_v2"] = blockDevice
|
||||
|
||||
return base, nil
|
||||
}
|
||||
|
||||
// Create requests the creation of a server from the given block device mapping.
|
||||
func Create(client *gophercloud.ServiceClient, opts servers.CreateOptsBuilder) servers.CreateResult {
|
||||
var res servers.CreateResult
|
||||
|
||||
reqBody, err := opts.ToServerCreateMap()
|
||||
if err != nil {
|
||||
res.Err = err
|
||||
return res
|
||||
}
|
||||
|
||||
// Delete imageName and flavorName that come from ToServerCreateMap().
|
||||
// As of Liberty, Boot From Volume is failing if they are passed.
|
||||
delete(reqBody["server"].(map[string]interface{}), "imageName")
|
||||
delete(reqBody["server"].(map[string]interface{}), "flavorName")
|
||||
|
||||
_, res.Err = client.Post(createURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200, 202},
|
||||
})
|
||||
return res
|
||||
}
|
10
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume/results.go
generated
vendored
Normal file
10
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume/results.go
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
package bootfromvolume
|
||||
|
||||
import (
|
||||
os "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
||||
)
|
||||
|
||||
// CreateResult temporarily contains the response from a Create call.
|
||||
type CreateResult struct {
|
||||
os.CreateResult
|
||||
}
|
7
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume/urls.go
generated
vendored
Normal file
7
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume/urls.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
package bootfromvolume
|
||||
|
||||
import "github.com/rackspace/gophercloud"
|
||||
|
||||
func createURL(c *gophercloud.ServiceClient) string {
|
||||
return c.ServiceURL("os-volumes_boot")
|
||||
}
|
1
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/defsecrules/doc.go
generated
vendored
Normal file
1
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/defsecrules/doc.go
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
package defsecrules
|
143
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/defsecrules/fixtures.go
generated
vendored
Normal file
143
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/defsecrules/fixtures.go
generated
vendored
Normal file
@@ -0,0 +1,143 @@
|
||||
package defsecrules
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
fake "github.com/rackspace/gophercloud/testhelper/client"
|
||||
)
|
||||
|
||||
const rootPath = "/os-security-group-default-rules"
|
||||
|
||||
func mockListRulesResponse(t *testing.T) {
|
||||
th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"security_group_default_rules": [
|
||||
{
|
||||
"from_port": 80,
|
||||
"id": "{ruleID}",
|
||||
"ip_protocol": "TCP",
|
||||
"ip_range": {
|
||||
"cidr": "10.10.10.0/24"
|
||||
},
|
||||
"to_port": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
`)
|
||||
})
|
||||
}
|
||||
|
||||
func mockCreateRuleResponse(t *testing.T) {
|
||||
th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"security_group_default_rule": {
|
||||
"ip_protocol": "TCP",
|
||||
"from_port": 80,
|
||||
"to_port": 80,
|
||||
"cidr": "10.10.12.0/24"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"security_group_default_rule": {
|
||||
"from_port": 80,
|
||||
"id": "{ruleID}",
|
||||
"ip_protocol": "TCP",
|
||||
"ip_range": {
|
||||
"cidr": "10.10.12.0/24"
|
||||
},
|
||||
"to_port": 80
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
}
|
||||
|
||||
func mockCreateRuleResponseICMPZero(t *testing.T) {
|
||||
th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"security_group_default_rule": {
|
||||
"ip_protocol": "ICMP",
|
||||
"from_port": 0,
|
||||
"to_port": 0,
|
||||
"cidr": "10.10.12.0/24"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"security_group_default_rule": {
|
||||
"from_port": 0,
|
||||
"id": "{ruleID}",
|
||||
"ip_protocol": "ICMP",
|
||||
"ip_range": {
|
||||
"cidr": "10.10.12.0/24"
|
||||
},
|
||||
"to_port": 0
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
}
|
||||
|
||||
func mockGetRuleResponse(t *testing.T, ruleID string) {
|
||||
url := rootPath + "/" + ruleID
|
||||
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"security_group_default_rule": {
|
||||
"id": "{ruleID}",
|
||||
"from_port": 80,
|
||||
"to_port": 80,
|
||||
"ip_protocol": "TCP",
|
||||
"ip_range": {
|
||||
"cidr": "10.10.12.0/24"
|
||||
}
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
}
|
||||
|
||||
func mockDeleteRuleResponse(t *testing.T, ruleID string) {
|
||||
url := rootPath + "/" + ruleID
|
||||
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "DELETE")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
})
|
||||
}
|
96
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/defsecrules/requests.go
generated
vendored
Normal file
96
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/defsecrules/requests.go
generated
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
package defsecrules
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// List will return a collection of default rules.
|
||||
func List(client *gophercloud.ServiceClient) pagination.Pager {
|
||||
createPage := func(r pagination.PageResult) pagination.Page {
|
||||
return DefaultRulePage{pagination.SinglePageBase(r)}
|
||||
}
|
||||
|
||||
return pagination.NewPager(client, rootURL(client), createPage)
|
||||
}
|
||||
|
||||
// CreateOpts represents the configuration for adding a new default rule.
|
||||
type CreateOpts struct {
|
||||
// Required - the lower bound of the port range that will be opened.
|
||||
FromPort int `json:"from_port"`
|
||||
|
||||
// Required - the upper bound of the port range that will be opened.
|
||||
ToPort int `json:"to_port"`
|
||||
|
||||
// Required - the protocol type that will be allowed, e.g. TCP.
|
||||
IPProtocol string `json:"ip_protocol"`
|
||||
|
||||
// ONLY required if FromGroupID is blank. This represents the IP range that
|
||||
// will be the source of network traffic to your security group. Use
|
||||
// 0.0.0.0/0 to allow all IP addresses.
|
||||
CIDR string `json:"cidr,omitempty"`
|
||||
}
|
||||
|
||||
// CreateOptsBuilder builds the create rule options into a serializable format.
|
||||
type CreateOptsBuilder interface {
|
||||
ToRuleCreateMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// ToRuleCreateMap builds the create rule options into a serializable format.
|
||||
func (opts CreateOpts) ToRuleCreateMap() (map[string]interface{}, error) {
|
||||
rule := make(map[string]interface{})
|
||||
|
||||
if opts.FromPort == 0 && strings.ToUpper(opts.IPProtocol) != "ICMP" {
|
||||
return rule, errors.New("A FromPort must be set")
|
||||
}
|
||||
if opts.ToPort == 0 && strings.ToUpper(opts.IPProtocol) != "ICMP" {
|
||||
return rule, errors.New("A ToPort must be set")
|
||||
}
|
||||
if opts.IPProtocol == "" {
|
||||
return rule, errors.New("A IPProtocol must be set")
|
||||
}
|
||||
if opts.CIDR == "" {
|
||||
return rule, errors.New("A CIDR must be set")
|
||||
}
|
||||
|
||||
rule["from_port"] = opts.FromPort
|
||||
rule["to_port"] = opts.ToPort
|
||||
rule["ip_protocol"] = opts.IPProtocol
|
||||
rule["cidr"] = opts.CIDR
|
||||
|
||||
return map[string]interface{}{"security_group_default_rule": rule}, nil
|
||||
}
|
||||
|
||||
// Create is the operation responsible for creating a new default rule.
|
||||
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
|
||||
var result CreateResult
|
||||
|
||||
reqBody, err := opts.ToRuleCreateMap()
|
||||
if err != nil {
|
||||
result.Err = err
|
||||
return result
|
||||
}
|
||||
|
||||
_, result.Err = client.Post(rootURL(client), reqBody, &result.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200},
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Get will return details for a particular default rule.
|
||||
func Get(client *gophercloud.ServiceClient, id string) GetResult {
|
||||
var result GetResult
|
||||
_, result.Err = client.Get(resourceURL(client, id), &result.Body, nil)
|
||||
return result
|
||||
}
|
||||
|
||||
// Delete will permanently delete a default rule from the project.
|
||||
func Delete(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult {
|
||||
var result gophercloud.ErrResult
|
||||
_, result.Err = client.Delete(resourceURL(client, id), nil)
|
||||
return result
|
||||
}
|
69
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/defsecrules/results.go
generated
vendored
Normal file
69
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/defsecrules/results.go
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
package defsecrules
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/mapstructure"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// DefaultRule represents a default rule - which is identical to a
|
||||
// normal security rule.
|
||||
type DefaultRule secgroups.Rule
|
||||
|
||||
// DefaultRulePage is a single page of a DefaultRule collection.
|
||||
type DefaultRulePage struct {
|
||||
pagination.SinglePageBase
|
||||
}
|
||||
|
||||
// IsEmpty determines whether or not a page of default rules contains any results.
|
||||
func (page DefaultRulePage) IsEmpty() (bool, error) {
|
||||
users, err := ExtractDefaultRules(page)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return len(users) == 0, nil
|
||||
}
|
||||
|
||||
// ExtractDefaultRules returns a slice of DefaultRules contained in a single
|
||||
// page of results.
|
||||
func ExtractDefaultRules(page pagination.Page) ([]DefaultRule, error) {
|
||||
casted := page.(DefaultRulePage).Body
|
||||
var response struct {
|
||||
Rules []DefaultRule `mapstructure:"security_group_default_rules"`
|
||||
}
|
||||
|
||||
err := mapstructure.WeakDecode(casted, &response)
|
||||
|
||||
return response.Rules, err
|
||||
}
|
||||
|
||||
type commonResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// CreateResult represents the result of a create operation.
|
||||
type CreateResult struct {
|
||||
commonResult
|
||||
}
|
||||
|
||||
// GetResult represents the result of a get operation.
|
||||
type GetResult struct {
|
||||
commonResult
|
||||
}
|
||||
|
||||
// Extract will extract a DefaultRule struct from most responses.
|
||||
func (r commonResult) Extract() (*DefaultRule, error) {
|
||||
if r.Err != nil {
|
||||
return nil, r.Err
|
||||
}
|
||||
|
||||
var response struct {
|
||||
Rule DefaultRule `mapstructure:"security_group_default_rule"`
|
||||
}
|
||||
|
||||
err := mapstructure.WeakDecode(r.Body, &response)
|
||||
|
||||
return &response.Rule, err
|
||||
}
|
13
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/defsecrules/urls.go
generated
vendored
Normal file
13
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/defsecrules/urls.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
package defsecrules
|
||||
|
||||
import "github.com/rackspace/gophercloud"
|
||||
|
||||
const rulepath = "os-security-group-default-rules"
|
||||
|
||||
func resourceURL(c *gophercloud.ServiceClient, id string) string {
|
||||
return c.ServiceURL(rulepath, id)
|
||||
}
|
||||
|
||||
func rootURL(c *gophercloud.ServiceClient) string {
|
||||
return c.ServiceURL(rulepath)
|
||||
}
|
23
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/delegate.go
generated
vendored
Normal file
23
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/delegate.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
package extensions
|
||||
|
||||
import (
|
||||
"github.com/rackspace/gophercloud"
|
||||
common "github.com/rackspace/gophercloud/openstack/common/extensions"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// ExtractExtensions interprets a Page as a slice of Extensions.
|
||||
func ExtractExtensions(page pagination.Page) ([]common.Extension, error) {
|
||||
return common.ExtractExtensions(page)
|
||||
}
|
||||
|
||||
// Get retrieves information for a specific extension using its alias.
|
||||
func Get(c *gophercloud.ServiceClient, alias string) common.GetResult {
|
||||
return common.Get(c, alias)
|
||||
}
|
||||
|
||||
// List returns a Pager which allows you to iterate over the full collection of extensions.
|
||||
// It does not accept query parameters.
|
||||
func List(c *gophercloud.ServiceClient) pagination.Pager {
|
||||
return common.List(c)
|
||||
}
|
3
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/diskconfig/doc.go
generated
vendored
Normal file
3
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/diskconfig/doc.go
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
// Package diskconfig provides information and interaction with the Disk
|
||||
// Config extension that works with the OpenStack Compute service.
|
||||
package diskconfig
|
114
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/diskconfig/requests.go
generated
vendored
Normal file
114
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/diskconfig/requests.go
generated
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
package diskconfig
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
||||
)
|
||||
|
||||
// DiskConfig represents one of the two possible settings for the DiskConfig option when creating,
|
||||
// rebuilding, or resizing servers: Auto or Manual.
|
||||
type DiskConfig string
|
||||
|
||||
const (
|
||||
// Auto builds a server with a single partition the size of the target flavor disk and
|
||||
// automatically adjusts the filesystem to fit the entire partition. Auto may only be used with
|
||||
// images and servers that use a single EXT3 partition.
|
||||
Auto DiskConfig = "AUTO"
|
||||
|
||||
// Manual builds a server using whatever partition scheme and filesystem are present in the source
|
||||
// image. If the target flavor disk is larger, the remaining space is left unpartitioned. This
|
||||
// enables images to have non-EXT3 filesystems, multiple partitions, and so on, and enables you
|
||||
// to manage the disk configuration. It also results in slightly shorter boot times.
|
||||
Manual DiskConfig = "MANUAL"
|
||||
)
|
||||
|
||||
// ErrInvalidDiskConfig is returned if an invalid string is specified for a DiskConfig option.
|
||||
var ErrInvalidDiskConfig = errors.New("DiskConfig must be either diskconfig.Auto or diskconfig.Manual.")
|
||||
|
||||
// Validate ensures that a DiskConfig contains an appropriate value.
|
||||
func (config DiskConfig) validate() error {
|
||||
switch config {
|
||||
case Auto, Manual:
|
||||
return nil
|
||||
default:
|
||||
return ErrInvalidDiskConfig
|
||||
}
|
||||
}
|
||||
|
||||
// CreateOptsExt adds a DiskConfig option to the base CreateOpts.
|
||||
type CreateOptsExt struct {
|
||||
servers.CreateOptsBuilder
|
||||
|
||||
// DiskConfig [optional] controls how the created server's disk is partitioned.
|
||||
DiskConfig DiskConfig `json:"OS-DCF:diskConfig,omitempty"`
|
||||
}
|
||||
|
||||
// ToServerCreateMap adds the diskconfig option 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 string(opts.DiskConfig) == "" {
|
||||
return base, nil
|
||||
}
|
||||
|
||||
serverMap := base["server"].(map[string]interface{})
|
||||
serverMap["OS-DCF:diskConfig"] = string(opts.DiskConfig)
|
||||
|
||||
return base, nil
|
||||
}
|
||||
|
||||
// RebuildOptsExt adds a DiskConfig option to the base RebuildOpts.
|
||||
type RebuildOptsExt struct {
|
||||
servers.RebuildOptsBuilder
|
||||
|
||||
// DiskConfig [optional] controls how the rebuilt server's disk is partitioned.
|
||||
DiskConfig DiskConfig
|
||||
}
|
||||
|
||||
// ToServerRebuildMap adds the diskconfig option to the base server rebuild options.
|
||||
func (opts RebuildOptsExt) ToServerRebuildMap() (map[string]interface{}, error) {
|
||||
err := opts.DiskConfig.validate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base, err := opts.RebuildOptsBuilder.ToServerRebuildMap()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
serverMap := base["rebuild"].(map[string]interface{})
|
||||
serverMap["OS-DCF:diskConfig"] = string(opts.DiskConfig)
|
||||
|
||||
return base, nil
|
||||
}
|
||||
|
||||
// ResizeOptsExt adds a DiskConfig option to the base server resize options.
|
||||
type ResizeOptsExt struct {
|
||||
servers.ResizeOptsBuilder
|
||||
|
||||
// DiskConfig [optional] controls how the resized server's disk is partitioned.
|
||||
DiskConfig DiskConfig
|
||||
}
|
||||
|
||||
// ToServerResizeMap adds the diskconfig option to the base server creation options.
|
||||
func (opts ResizeOptsExt) ToServerResizeMap() (map[string]interface{}, error) {
|
||||
err := opts.DiskConfig.validate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base, err := opts.ResizeOptsBuilder.ToServerResizeMap()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
serverMap := base["resize"].(map[string]interface{})
|
||||
serverMap["OS-DCF:diskConfig"] = string(opts.DiskConfig)
|
||||
|
||||
return base, nil
|
||||
}
|
60
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/diskconfig/results.go
generated
vendored
Normal file
60
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/diskconfig/results.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
package diskconfig
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
func commonExtract(result gophercloud.Result) (*DiskConfig, error) {
|
||||
var resp struct {
|
||||
Server struct {
|
||||
DiskConfig string `mapstructure:"OS-DCF:diskConfig"`
|
||||
} `mapstructure:"server"`
|
||||
}
|
||||
|
||||
err := mapstructure.Decode(result.Body, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config := DiskConfig(resp.Server.DiskConfig)
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
// ExtractGet returns the disk configuration from a servers.Get call.
|
||||
func ExtractGet(result servers.GetResult) (*DiskConfig, error) {
|
||||
return commonExtract(result.Result)
|
||||
}
|
||||
|
||||
// ExtractUpdate returns the disk configuration from a servers.Update call.
|
||||
func ExtractUpdate(result servers.UpdateResult) (*DiskConfig, error) {
|
||||
return commonExtract(result.Result)
|
||||
}
|
||||
|
||||
// ExtractRebuild returns the disk configuration from a servers.Rebuild call.
|
||||
func ExtractRebuild(result servers.RebuildResult) (*DiskConfig, error) {
|
||||
return commonExtract(result.Result)
|
||||
}
|
||||
|
||||
// ExtractDiskConfig returns the DiskConfig setting for a specific server acquired from an
|
||||
// servers.ExtractServers call, while iterating through a Pager.
|
||||
func ExtractDiskConfig(page pagination.Page, index int) (*DiskConfig, error) {
|
||||
casted := page.(servers.ServerPage).Body
|
||||
|
||||
type server struct {
|
||||
DiskConfig string `mapstructure:"OS-DCF:diskConfig"`
|
||||
}
|
||||
var response struct {
|
||||
Servers []server `mapstructure:"servers"`
|
||||
}
|
||||
|
||||
err := mapstructure.Decode(casted, &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config := DiskConfig(response.Servers[index].DiskConfig)
|
||||
return &config, nil
|
||||
}
|
3
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/doc.go
generated
vendored
Normal file
3
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/doc.go
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
// Package extensions provides information and interaction with the
|
||||
// different extensions available for the OpenStack Compute service.
|
||||
package extensions
|
3
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip/doc.go
generated
vendored
Normal file
3
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip/doc.go
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
// Package floatingip provides the ability to manage floating ips through
|
||||
// nova-network
|
||||
package floatingip
|
193
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip/fixtures.go
generated
vendored
Normal file
193
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip/fixtures.go
generated
vendored
Normal file
@@ -0,0 +1,193 @@
|
||||
// +build fixtures
|
||||
|
||||
package floatingip
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
"github.com/rackspace/gophercloud/testhelper/client"
|
||||
)
|
||||
|
||||
// ListOutput is a sample response to a List call.
|
||||
const ListOutput = `
|
||||
{
|
||||
"floating_ips": [
|
||||
{
|
||||
"fixed_ip": null,
|
||||
"id": 1,
|
||||
"instance_id": null,
|
||||
"ip": "10.10.10.1",
|
||||
"pool": "nova"
|
||||
},
|
||||
{
|
||||
"fixed_ip": "166.78.185.201",
|
||||
"id": 2,
|
||||
"instance_id": "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
|
||||
"ip": "10.10.10.2",
|
||||
"pool": "nova"
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
|
||||
// GetOutput is a sample response to a Get call.
|
||||
const GetOutput = `
|
||||
{
|
||||
"floating_ip": {
|
||||
"fixed_ip": "166.78.185.201",
|
||||
"id": 2,
|
||||
"instance_id": "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
|
||||
"ip": "10.10.10.2",
|
||||
"pool": "nova"
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
// CreateOutput is a sample response to a Post call
|
||||
const CreateOutput = `
|
||||
{
|
||||
"floating_ip": {
|
||||
"fixed_ip": null,
|
||||
"id": 1,
|
||||
"instance_id": null,
|
||||
"ip": "10.10.10.1",
|
||||
"pool": "nova"
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
// FirstFloatingIP is the first result in ListOutput.
|
||||
var FirstFloatingIP = FloatingIP{
|
||||
ID: "1",
|
||||
IP: "10.10.10.1",
|
||||
Pool: "nova",
|
||||
}
|
||||
|
||||
// SecondFloatingIP is the first result in ListOutput.
|
||||
var SecondFloatingIP = FloatingIP{
|
||||
FixedIP: "166.78.185.201",
|
||||
ID: "2",
|
||||
InstanceID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
|
||||
IP: "10.10.10.2",
|
||||
Pool: "nova",
|
||||
}
|
||||
|
||||
// ExpectedFloatingIPsSlice is the slice of results that should be parsed
|
||||
// from ListOutput, in the expected order.
|
||||
var ExpectedFloatingIPsSlice = []FloatingIP{FirstFloatingIP, SecondFloatingIP}
|
||||
|
||||
// CreatedFloatingIP is the parsed result from CreateOutput.
|
||||
var CreatedFloatingIP = FloatingIP{
|
||||
ID: "1",
|
||||
IP: "10.10.10.1",
|
||||
Pool: "nova",
|
||||
}
|
||||
|
||||
// HandleListSuccessfully configures the test server to respond to a List request.
|
||||
func HandleListSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/os-floating-ips", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
fmt.Fprintf(w, ListOutput)
|
||||
})
|
||||
}
|
||||
|
||||
// HandleGetSuccessfully configures the test server to respond to a Get request
|
||||
// for an existing floating ip
|
||||
func HandleGetSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/os-floating-ips/2", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
fmt.Fprintf(w, GetOutput)
|
||||
})
|
||||
}
|
||||
|
||||
// HandleCreateSuccessfully configures the test server to respond to a Create request
|
||||
// for a new floating ip
|
||||
func HandleCreateSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/os-floating-ips", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"pool": "nova"
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
fmt.Fprintf(w, CreateOutput)
|
||||
})
|
||||
}
|
||||
|
||||
// HandleDeleteSuccessfully configures the test server to respond to a Delete request for a
|
||||
// an existing floating ip
|
||||
func HandleDeleteSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/os-floating-ips/1", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "DELETE")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
})
|
||||
}
|
||||
|
||||
// HandleAssociateSuccessfully configures the test server to respond to a Post request
|
||||
// to associate an allocated floating IP
|
||||
func HandleAssociateSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/action", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"addFloatingIp": {
|
||||
"address": "10.10.10.2"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
})
|
||||
}
|
||||
|
||||
// HandleFixedAssociateSucessfully configures the test server to respond to a Post request
|
||||
// to associate an allocated floating IP with a specific fixed IP address
|
||||
func HandleAssociateFixedSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/action", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"addFloatingIp": {
|
||||
"address": "10.10.10.2",
|
||||
"fixed_address": "166.78.185.201"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
})
|
||||
}
|
||||
|
||||
// HandleDisassociateSuccessfully configures the test server to respond to a Post request
|
||||
// to disassociate an allocated floating IP
|
||||
func HandleDisassociateSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/action", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"removeFloatingIp": {
|
||||
"address": "10.10.10.2"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
})
|
||||
}
|
171
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip/requests.go
generated
vendored
Normal file
171
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip/requests.go
generated
vendored
Normal file
@@ -0,0 +1,171 @@
|
||||
package floatingip
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// List returns a Pager that allows you to iterate over a collection of FloatingIPs.
|
||||
func List(client *gophercloud.ServiceClient) pagination.Pager {
|
||||
return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page {
|
||||
return FloatingIPsPage{pagination.SinglePageBase(r)}
|
||||
})
|
||||
}
|
||||
|
||||
// CreateOptsBuilder describes struct types that can be accepted by the Create call. Notable, the
|
||||
// CreateOpts struct in this package does.
|
||||
type CreateOptsBuilder interface {
|
||||
ToFloatingIPCreateMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// CreateOpts specifies a Floating IP allocation request
|
||||
type CreateOpts struct {
|
||||
// Pool is the pool of floating IPs to allocate one from
|
||||
Pool string
|
||||
}
|
||||
|
||||
// AssociateOpts specifies the required information to associate or disassociate a floating IP to an instance
|
||||
type AssociateOpts struct {
|
||||
// ServerID is the UUID of the server
|
||||
ServerID string
|
||||
|
||||
// FixedIP is an optional fixed IP address of the server
|
||||
FixedIP string
|
||||
|
||||
// FloatingIP is the floating IP to associate with an instance
|
||||
FloatingIP string
|
||||
}
|
||||
|
||||
// ToFloatingIPCreateMap constructs a request body from CreateOpts.
|
||||
func (opts CreateOpts) ToFloatingIPCreateMap() (map[string]interface{}, error) {
|
||||
if opts.Pool == "" {
|
||||
return nil, errors.New("Missing field required for floating IP creation: Pool")
|
||||
}
|
||||
|
||||
return map[string]interface{}{"pool": opts.Pool}, nil
|
||||
}
|
||||
|
||||
// ToAssociateMap constructs a request body from AssociateOpts.
|
||||
func (opts AssociateOpts) ToAssociateMap() (map[string]interface{}, error) {
|
||||
if opts.ServerID == "" {
|
||||
return nil, errors.New("Required field missing for floating IP association: ServerID")
|
||||
}
|
||||
|
||||
if opts.FloatingIP == "" {
|
||||
return nil, errors.New("Required field missing for floating IP association: FloatingIP")
|
||||
}
|
||||
|
||||
associateInfo := map[string]interface{}{
|
||||
"serverId": opts.ServerID,
|
||||
"floatingIp": opts.FloatingIP,
|
||||
"fixedIp": opts.FixedIP,
|
||||
}
|
||||
|
||||
return associateInfo, nil
|
||||
|
||||
}
|
||||
|
||||
// Create requests the creation of a new floating IP
|
||||
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
|
||||
var res CreateResult
|
||||
|
||||
reqBody, err := opts.ToFloatingIPCreateMap()
|
||||
if err != nil {
|
||||
res.Err = err
|
||||
return res
|
||||
}
|
||||
|
||||
_, res.Err = client.Post(createURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200},
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
// Get returns data about a previously created FloatingIP.
|
||||
func Get(client *gophercloud.ServiceClient, id string) GetResult {
|
||||
var res GetResult
|
||||
_, res.Err = client.Get(getURL(client, id), &res.Body, nil)
|
||||
return res
|
||||
}
|
||||
|
||||
// Delete requests the deletion of a previous allocated FloatingIP.
|
||||
func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
|
||||
var res DeleteResult
|
||||
_, res.Err = client.Delete(deleteURL(client, id), nil)
|
||||
return res
|
||||
}
|
||||
|
||||
// association / disassociation
|
||||
|
||||
// Associate pairs an allocated floating IP with an instance
|
||||
// Deprecated. Use AssociateInstance.
|
||||
func Associate(client *gophercloud.ServiceClient, serverId, fip string) AssociateResult {
|
||||
var res AssociateResult
|
||||
|
||||
addFloatingIp := make(map[string]interface{})
|
||||
addFloatingIp["address"] = fip
|
||||
reqBody := map[string]interface{}{"addFloatingIp": addFloatingIp}
|
||||
|
||||
_, res.Err = client.Post(associateURL(client, serverId), reqBody, nil, nil)
|
||||
return res
|
||||
}
|
||||
|
||||
// AssociateInstance pairs an allocated floating IP with an instance.
|
||||
func AssociateInstance(client *gophercloud.ServiceClient, opts AssociateOpts) AssociateResult {
|
||||
var res AssociateResult
|
||||
|
||||
associateInfo, err := opts.ToAssociateMap()
|
||||
if err != nil {
|
||||
res.Err = err
|
||||
return res
|
||||
}
|
||||
|
||||
addFloatingIp := make(map[string]interface{})
|
||||
addFloatingIp["address"] = associateInfo["floatingIp"].(string)
|
||||
|
||||
// fixedIp is not required
|
||||
if associateInfo["fixedIp"] != "" {
|
||||
addFloatingIp["fixed_address"] = associateInfo["fixedIp"].(string)
|
||||
}
|
||||
|
||||
serverId := associateInfo["serverId"].(string)
|
||||
|
||||
reqBody := map[string]interface{}{"addFloatingIp": addFloatingIp}
|
||||
_, res.Err = client.Post(associateURL(client, serverId), reqBody, nil, nil)
|
||||
return res
|
||||
}
|
||||
|
||||
// Disassociate decouples an allocated floating IP from an instance
|
||||
// Deprecated. Use DisassociateInstance.
|
||||
func Disassociate(client *gophercloud.ServiceClient, serverId, fip string) DisassociateResult {
|
||||
var res DisassociateResult
|
||||
|
||||
removeFloatingIp := make(map[string]interface{})
|
||||
removeFloatingIp["address"] = fip
|
||||
reqBody := map[string]interface{}{"removeFloatingIp": removeFloatingIp}
|
||||
|
||||
_, res.Err = client.Post(disassociateURL(client, serverId), reqBody, nil, nil)
|
||||
return res
|
||||
}
|
||||
|
||||
// DisassociateInstance decouples an allocated floating IP from an instance
|
||||
func DisassociateInstance(client *gophercloud.ServiceClient, opts AssociateOpts) DisassociateResult {
|
||||
var res DisassociateResult
|
||||
|
||||
associateInfo, err := opts.ToAssociateMap()
|
||||
if err != nil {
|
||||
res.Err = err
|
||||
return res
|
||||
}
|
||||
|
||||
removeFloatingIp := make(map[string]interface{})
|
||||
removeFloatingIp["address"] = associateInfo["floatingIp"].(string)
|
||||
reqBody := map[string]interface{}{"removeFloatingIp": removeFloatingIp}
|
||||
|
||||
serverId := associateInfo["serverId"].(string)
|
||||
|
||||
_, res.Err = client.Post(disassociateURL(client, serverId), reqBody, nil, nil)
|
||||
return res
|
||||
}
|
99
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip/results.go
generated
vendored
Normal file
99
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip/results.go
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
package floatingip
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// A FloatingIP is an IP that can be associated with an instance
|
||||
type FloatingIP struct {
|
||||
// ID is a unique ID of the Floating IP
|
||||
ID string `mapstructure:"id"`
|
||||
|
||||
// FixedIP is the IP of the instance related to the Floating IP
|
||||
FixedIP string `mapstructure:"fixed_ip,omitempty"`
|
||||
|
||||
// InstanceID is the ID of the instance that is using the Floating IP
|
||||
InstanceID string `mapstructure:"instance_id"`
|
||||
|
||||
// IP is the actual Floating IP
|
||||
IP string `mapstructure:"ip"`
|
||||
|
||||
// Pool is the pool of floating IPs that this floating IP belongs to
|
||||
Pool string `mapstructure:"pool"`
|
||||
}
|
||||
|
||||
// FloatingIPsPage stores a single, only page of FloatingIPs
|
||||
// results from a List call.
|
||||
type FloatingIPsPage struct {
|
||||
pagination.SinglePageBase
|
||||
}
|
||||
|
||||
// IsEmpty determines whether or not a FloatingIPsPage is empty.
|
||||
func (page FloatingIPsPage) IsEmpty() (bool, error) {
|
||||
va, err := ExtractFloatingIPs(page)
|
||||
return len(va) == 0, err
|
||||
}
|
||||
|
||||
// ExtractFloatingIPs interprets a page of results as a slice of
|
||||
// FloatingIPs.
|
||||
func ExtractFloatingIPs(page pagination.Page) ([]FloatingIP, error) {
|
||||
casted := page.(FloatingIPsPage).Body
|
||||
var response struct {
|
||||
FloatingIPs []FloatingIP `mapstructure:"floating_ips"`
|
||||
}
|
||||
|
||||
err := mapstructure.WeakDecode(casted, &response)
|
||||
|
||||
return response.FloatingIPs, err
|
||||
}
|
||||
|
||||
type FloatingIPResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// Extract is a method that attempts to interpret any FloatingIP resource
|
||||
// response as a FloatingIP struct.
|
||||
func (r FloatingIPResult) Extract() (*FloatingIP, error) {
|
||||
if r.Err != nil {
|
||||
return nil, r.Err
|
||||
}
|
||||
|
||||
var res struct {
|
||||
FloatingIP *FloatingIP `json:"floating_ip" mapstructure:"floating_ip"`
|
||||
}
|
||||
|
||||
err := mapstructure.WeakDecode(r.Body, &res)
|
||||
return res.FloatingIP, err
|
||||
}
|
||||
|
||||
// CreateResult is the response from a Create operation. Call its Extract method to interpret it
|
||||
// as a FloatingIP.
|
||||
type CreateResult struct {
|
||||
FloatingIPResult
|
||||
}
|
||||
|
||||
// GetResult is the response from a Get operation. Call its Extract method to interpret it
|
||||
// as a FloatingIP.
|
||||
type GetResult struct {
|
||||
FloatingIPResult
|
||||
}
|
||||
|
||||
// DeleteResult is the response from a Delete operation. Call its Extract method to determine if
|
||||
// the call succeeded or failed.
|
||||
type DeleteResult struct {
|
||||
gophercloud.ErrResult
|
||||
}
|
||||
|
||||
// AssociateResult is the response from a Delete operation. Call its Extract method to determine if
|
||||
// the call succeeded or failed.
|
||||
type AssociateResult struct {
|
||||
gophercloud.ErrResult
|
||||
}
|
||||
|
||||
// DisassociateResult is the response from a Delete operation. Call its Extract method to determine if
|
||||
// the call succeeded or failed.
|
||||
type DisassociateResult struct {
|
||||
gophercloud.ErrResult
|
||||
}
|
37
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip/urls.go
generated
vendored
Normal file
37
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip/urls.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
package floatingip
|
||||
|
||||
import "github.com/rackspace/gophercloud"
|
||||
|
||||
const resourcePath = "os-floating-ips"
|
||||
|
||||
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, id string) string {
|
||||
return c.ServiceURL(resourcePath, id)
|
||||
}
|
||||
|
||||
func deleteURL(c *gophercloud.ServiceClient, id string) string {
|
||||
return getURL(c, id)
|
||||
}
|
||||
|
||||
func serverURL(c *gophercloud.ServiceClient, serverId string) string {
|
||||
return c.ServiceURL("servers/" + serverId + "/action")
|
||||
}
|
||||
|
||||
func associateURL(c *gophercloud.ServiceClient, serverId string) string {
|
||||
return serverURL(c, serverId)
|
||||
}
|
||||
|
||||
func disassociateURL(c *gophercloud.ServiceClient, serverId string) string {
|
||||
return serverURL(c, serverId)
|
||||
}
|
3
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs/doc.go
generated
vendored
Normal file
3
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs/doc.go
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
// Package keypairs provides information and interaction with the Keypairs
|
||||
// extension for the OpenStack Compute service.
|
||||
package keypairs
|
171
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs/fixtures.go
generated
vendored
Normal file
171
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs/fixtures.go
generated
vendored
Normal file
@@ -0,0 +1,171 @@
|
||||
// +build fixtures
|
||||
|
||||
package keypairs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
"github.com/rackspace/gophercloud/testhelper/client"
|
||||
)
|
||||
|
||||
// ListOutput is a sample response to a List call.
|
||||
const ListOutput = `
|
||||
{
|
||||
"keypairs": [
|
||||
{
|
||||
"keypair": {
|
||||
"fingerprint": "15:b0:f8:b3:f9:48:63:71:cf:7b:5b:38:6d:44:2d:4a",
|
||||
"name": "firstkey",
|
||||
"public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC+Eo/RZRngaGTkFs7I62ZjsIlO79KklKbMXi8F+KITD4bVQHHn+kV+4gRgkgCRbdoDqoGfpaDFs877DYX9n4z6FrAIZ4PES8TNKhatifpn9NdQYWA+IkU8CuvlEKGuFpKRi/k7JLos/gHi2hy7QUwgtRvcefvD/vgQZOVw/mGR9Q== Generated by Nova\n"
|
||||
}
|
||||
},
|
||||
{
|
||||
"keypair": {
|
||||
"fingerprint": "35:9d:d0:c3:4a:80:d3:d8:86:f1:ca:f7:df:c4:f9:d8",
|
||||
"name": "secondkey",
|
||||
"public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7DUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5Q== Generated by Nova\n"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
|
||||
// GetOutput is a sample response to a Get call.
|
||||
const GetOutput = `
|
||||
{
|
||||
"keypair": {
|
||||
"public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC+Eo/RZRngaGTkFs7I62ZjsIlO79KklKbMXi8F+KITD4bVQHHn+kV+4gRgkgCRbdoDqoGfpaDFs877DYX9n4z6FrAIZ4PES8TNKhatifpn9NdQYWA+IkU8CuvlEKGuFpKRi/k7JLos/gHi2hy7QUwgtRvcefvD/vgQZOVw/mGR9Q== Generated by Nova\n",
|
||||
"name": "firstkey",
|
||||
"fingerprint": "15:b0:f8:b3:f9:48:63:71:cf:7b:5b:38:6d:44:2d:4a"
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
// CreateOutput is a sample response to a Create call.
|
||||
const CreateOutput = `
|
||||
{
|
||||
"keypair": {
|
||||
"fingerprint": "35:9d:d0:c3:4a:80:d3:d8:86:f1:ca:f7:df:c4:f9:d8",
|
||||
"name": "createdkey",
|
||||
"private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7\nDUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ\n9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5QIDAQAB\nAoGAE5XO1mDhORy9COvsg+kYPUhB1GsCYxh+v88wG7HeFDKBY6KUc/Kxo6yoGn5T\nTjRjekyi2KoDZHz4VlIzyZPwFS4I1bf3oCunVoAKzgLdmnTtvRNMC5jFOGc2vUgP\n9bSyRj3S1R4ClVk2g0IDeagko/jc8zzLEYuIK+fbkds79YECQQDt3vcevgegnkga\ntF4NsDmmBPRkcSHCqrANP/7vFcBQN3czxeYYWX3DK07alu6GhH1Y4sHbdm616uU0\nll7xbDzxAkEAzAtN2IyftNygV2EGiaGgqLyo/tD9+Vui2qCQplqe4jvWh/5Sparl\nOjmKo+uAW+hLrLVMnHzRWxbWU8hirH5FNQJATO+ZxCK4etXXAnQmG41NCAqANWB2\nB+2HJbH2NcQ2QHvAHUm741JGn/KI/aBlo7KEjFRDWUVUB5ji64BbUwCsMQJBAIku\nLGcjnBf/oLk+XSPZC2eGd2Ph5G5qYmH0Q2vkTx+wtTn3DV+eNsDfgMtWAJVJ5t61\ngU1QSXyhLPVlKpnnxuUCQC+xvvWjWtsLaFtAsZywJiqLxQzHts8XLGZptYJ5tLWV\nrtmYtBcJCN48RrgQHry/xWYeA4K/AFQpXfNPgprQ96Q=\n-----END RSA PRIVATE KEY-----\n",
|
||||
"public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7DUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5Q== Generated by Nova\n",
|
||||
"user_id": "fake"
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
// ImportOutput is a sample response to a Create call that provides its own public key.
|
||||
const ImportOutput = `
|
||||
{
|
||||
"keypair": {
|
||||
"fingerprint": "1e:2c:9b:56:79:4b:45:77:f9:ca:7a:98:2c:b0:d5:3c",
|
||||
"name": "importedkey",
|
||||
"public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDx8nkQv/zgGgB4rMYmIf+6A4l6Rr+o/6lHBQdW5aYd44bd8JttDCE/F/pNRr0lRE+PiqSPO8nDPHw0010JeMH9gYgnnFlyY3/OcJ02RhIPyyxYpv9FhY+2YiUkpwFOcLImyrxEsYXpD/0d3ac30bNH6Sw9JD9UZHYcpSxsIbECHw== Generated by Nova",
|
||||
"user_id": "fake"
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
// FirstKeyPair is the first result in ListOutput.
|
||||
var FirstKeyPair = KeyPair{
|
||||
Name: "firstkey",
|
||||
Fingerprint: "15:b0:f8:b3:f9:48:63:71:cf:7b:5b:38:6d:44:2d:4a",
|
||||
PublicKey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC+Eo/RZRngaGTkFs7I62ZjsIlO79KklKbMXi8F+KITD4bVQHHn+kV+4gRgkgCRbdoDqoGfpaDFs877DYX9n4z6FrAIZ4PES8TNKhatifpn9NdQYWA+IkU8CuvlEKGuFpKRi/k7JLos/gHi2hy7QUwgtRvcefvD/vgQZOVw/mGR9Q== Generated by Nova\n",
|
||||
}
|
||||
|
||||
// SecondKeyPair is the second result in ListOutput.
|
||||
var SecondKeyPair = KeyPair{
|
||||
Name: "secondkey",
|
||||
Fingerprint: "35:9d:d0:c3:4a:80:d3:d8:86:f1:ca:f7:df:c4:f9:d8",
|
||||
PublicKey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7DUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5Q== Generated by Nova\n",
|
||||
}
|
||||
|
||||
// ExpectedKeyPairSlice is the slice of results that should be parsed from ListOutput, in the expected
|
||||
// order.
|
||||
var ExpectedKeyPairSlice = []KeyPair{FirstKeyPair, SecondKeyPair}
|
||||
|
||||
// CreatedKeyPair is the parsed result from CreatedOutput.
|
||||
var CreatedKeyPair = KeyPair{
|
||||
Name: "createdkey",
|
||||
Fingerprint: "35:9d:d0:c3:4a:80:d3:d8:86:f1:ca:f7:df:c4:f9:d8",
|
||||
PublicKey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7DUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5Q== Generated by Nova\n",
|
||||
PrivateKey: "-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7\nDUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ\n9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5QIDAQAB\nAoGAE5XO1mDhORy9COvsg+kYPUhB1GsCYxh+v88wG7HeFDKBY6KUc/Kxo6yoGn5T\nTjRjekyi2KoDZHz4VlIzyZPwFS4I1bf3oCunVoAKzgLdmnTtvRNMC5jFOGc2vUgP\n9bSyRj3S1R4ClVk2g0IDeagko/jc8zzLEYuIK+fbkds79YECQQDt3vcevgegnkga\ntF4NsDmmBPRkcSHCqrANP/7vFcBQN3czxeYYWX3DK07alu6GhH1Y4sHbdm616uU0\nll7xbDzxAkEAzAtN2IyftNygV2EGiaGgqLyo/tD9+Vui2qCQplqe4jvWh/5Sparl\nOjmKo+uAW+hLrLVMnHzRWxbWU8hirH5FNQJATO+ZxCK4etXXAnQmG41NCAqANWB2\nB+2HJbH2NcQ2QHvAHUm741JGn/KI/aBlo7KEjFRDWUVUB5ji64BbUwCsMQJBAIku\nLGcjnBf/oLk+XSPZC2eGd2Ph5G5qYmH0Q2vkTx+wtTn3DV+eNsDfgMtWAJVJ5t61\ngU1QSXyhLPVlKpnnxuUCQC+xvvWjWtsLaFtAsZywJiqLxQzHts8XLGZptYJ5tLWV\nrtmYtBcJCN48RrgQHry/xWYeA4K/AFQpXfNPgprQ96Q=\n-----END RSA PRIVATE KEY-----\n",
|
||||
UserID: "fake",
|
||||
}
|
||||
|
||||
// ImportedKeyPair is the parsed result from ImportOutput.
|
||||
var ImportedKeyPair = KeyPair{
|
||||
Name: "importedkey",
|
||||
Fingerprint: "1e:2c:9b:56:79:4b:45:77:f9:ca:7a:98:2c:b0:d5:3c",
|
||||
PublicKey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDx8nkQv/zgGgB4rMYmIf+6A4l6Rr+o/6lHBQdW5aYd44bd8JttDCE/F/pNRr0lRE+PiqSPO8nDPHw0010JeMH9gYgnnFlyY3/OcJ02RhIPyyxYpv9FhY+2YiUkpwFOcLImyrxEsYXpD/0d3ac30bNH6Sw9JD9UZHYcpSxsIbECHw== Generated by Nova",
|
||||
UserID: "fake",
|
||||
}
|
||||
|
||||
// HandleListSuccessfully configures the test server to respond to a List request.
|
||||
func HandleListSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/os-keypairs", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
fmt.Fprintf(w, ListOutput)
|
||||
})
|
||||
}
|
||||
|
||||
// HandleGetSuccessfully configures the test server to respond to a Get request for "firstkey".
|
||||
func HandleGetSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/os-keypairs/firstkey", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
fmt.Fprintf(w, GetOutput)
|
||||
})
|
||||
}
|
||||
|
||||
// HandleCreateSuccessfully configures the test server to respond to a Create request for a new
|
||||
// keypair called "createdkey".
|
||||
func HandleCreateSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/os-keypairs", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
th.TestJSONRequest(t, r, `{ "keypair": { "name": "createdkey" } }`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
fmt.Fprintf(w, CreateOutput)
|
||||
})
|
||||
}
|
||||
|
||||
// HandleImportSuccessfully configures the test server to respond to an Import request for an
|
||||
// existing keypair called "importedkey".
|
||||
func HandleImportSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/os-keypairs", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"keypair": {
|
||||
"name": "importedkey",
|
||||
"public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDx8nkQv/zgGgB4rMYmIf+6A4l6Rr+o/6lHBQdW5aYd44bd8JttDCE/F/pNRr0lRE+PiqSPO8nDPHw0010JeMH9gYgnnFlyY3/OcJ02RhIPyyxYpv9FhY+2YiUkpwFOcLImyrxEsYXpD/0d3ac30bNH6Sw9JD9UZHYcpSxsIbECHw== Generated by Nova"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
fmt.Fprintf(w, ImportOutput)
|
||||
})
|
||||
}
|
||||
|
||||
// HandleDeleteSuccessfully configures the test server to respond to a Delete request for a
|
||||
// keypair called "deletedkey".
|
||||
func HandleDeleteSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/os-keypairs/deletedkey", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "DELETE")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
})
|
||||
}
|
102
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs/requests.go
generated
vendored
Normal file
102
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs/requests.go
generated
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
package keypairs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// CreateOptsExt adds a KeyPair option to the base CreateOpts.
|
||||
type CreateOptsExt struct {
|
||||
servers.CreateOptsBuilder
|
||||
KeyName string `json:"key_name,omitempty"`
|
||||
}
|
||||
|
||||
// ToServerCreateMap adds the key_name and, optionally, key_data options 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 describes struct types that can be accepted by the Create call. Notable, the
|
||||
// CreateOpts struct in this package does.
|
||||
type CreateOptsBuilder interface {
|
||||
ToKeyPairCreateMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// CreateOpts specifies keypair creation or import parameters.
|
||||
type CreateOpts struct {
|
||||
// Name [required] is a friendly name to refer to this KeyPair in other services.
|
||||
Name string
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// ToKeyPairCreateMap constructs a request body from CreateOpts.
|
||||
func (opts CreateOpts) ToKeyPairCreateMap() (map[string]interface{}, error) {
|
||||
if opts.Name == "" {
|
||||
return nil, errors.New("Missing field required for keypair creation: Name")
|
||||
}
|
||||
|
||||
keypair := make(map[string]interface{})
|
||||
keypair["name"] = opts.Name
|
||||
if opts.PublicKey != "" {
|
||||
keypair["public_key"] = opts.PublicKey
|
||||
}
|
||||
|
||||
return map[string]interface{}{"keypair": keypair}, nil
|
||||
}
|
||||
|
||||
// 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) CreateResult {
|
||||
var res CreateResult
|
||||
|
||||
reqBody, err := opts.ToKeyPairCreateMap()
|
||||
if err != nil {
|
||||
res.Err = err
|
||||
return res
|
||||
}
|
||||
|
||||
_, res.Err = client.Post(createURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200},
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
// Get returns public data about a previously uploaded KeyPair.
|
||||
func Get(client *gophercloud.ServiceClient, name string) GetResult {
|
||||
var res GetResult
|
||||
_, res.Err = client.Get(getURL(client, name), &res.Body, nil)
|
||||
return res
|
||||
}
|
||||
|
||||
// Delete requests the deletion of a previous stored KeyPair from the server.
|
||||
func Delete(client *gophercloud.ServiceClient, name string) DeleteResult {
|
||||
var res DeleteResult
|
||||
_, res.Err = client.Delete(deleteURL(client, name), nil)
|
||||
return res
|
||||
}
|
94
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs/results.go
generated
vendored
Normal file
94
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs/results.go
generated
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
package keypairs
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// KeyPair is an SSH key known to the OpenStack cluster 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 `mapstructure:"name"`
|
||||
|
||||
// Fingerprint is a short sequence of bytes that can be used to authenticate or validate a longer
|
||||
// public key.
|
||||
Fingerprint string `mapstructure:"fingerprint"`
|
||||
|
||||
// PublicKey is the public key from this pair, in OpenSSH format. "ssh-rsa AAAAB3Nz..."
|
||||
PublicKey string `mapstructure:"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 `mapstructure:"private_key"`
|
||||
|
||||
// UserID is the user who owns this keypair.
|
||||
UserID string `mapstructure:"user_id"`
|
||||
}
|
||||
|
||||
// KeyPairPage stores a single, only page of KeyPair results from a List call.
|
||||
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(page pagination.Page) ([]KeyPair, error) {
|
||||
type pair struct {
|
||||
KeyPair KeyPair `mapstructure:"keypair"`
|
||||
}
|
||||
|
||||
var resp struct {
|
||||
KeyPairs []pair `mapstructure:"keypairs"`
|
||||
}
|
||||
|
||||
err := mapstructure.Decode(page.(KeyPairPage).Body, &resp)
|
||||
results := make([]KeyPair, len(resp.KeyPairs))
|
||||
for i, pair := range resp.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) {
|
||||
if r.Err != nil {
|
||||
return nil, r.Err
|
||||
}
|
||||
|
||||
var res struct {
|
||||
KeyPair *KeyPair `json:"keypair" mapstructure:"keypair"`
|
||||
}
|
||||
|
||||
err := mapstructure.Decode(r.Body, &res)
|
||||
return res.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 Extract method to determine if
|
||||
// the call succeeded or failed.
|
||||
type DeleteResult struct {
|
||||
gophercloud.ErrResult
|
||||
}
|
25
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs/urls.go
generated
vendored
Normal file
25
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs/urls.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
package keypairs
|
||||
|
||||
import "github.com/rackspace/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)
|
||||
}
|
2
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/networks/doc.go
generated
vendored
Normal file
2
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/networks/doc.go
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
// Package network provides the ability to manage nova-networks
|
||||
package networks
|
209
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/networks/fixtures.go
generated
vendored
Normal file
209
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/networks/fixtures.go
generated
vendored
Normal file
@@ -0,0 +1,209 @@
|
||||
// +build fixtures
|
||||
|
||||
package networks
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
"github.com/rackspace/gophercloud/testhelper/client"
|
||||
)
|
||||
|
||||
// ListOutput is a sample response to a List call.
|
||||
const ListOutput = `
|
||||
{
|
||||
"networks": [
|
||||
{
|
||||
"bridge": "br100",
|
||||
"bridge_interface": "eth0",
|
||||
"broadcast": "10.0.0.7",
|
||||
"cidr": "10.0.0.0/29",
|
||||
"cidr_v6": null,
|
||||
"created_at": "2011-08-15 06:19:19.387525",
|
||||
"deleted": false,
|
||||
"deleted_at": null,
|
||||
"dhcp_start": "10.0.0.3",
|
||||
"dns1": null,
|
||||
"dns2": null,
|
||||
"gateway": "10.0.0.1",
|
||||
"gateway_v6": null,
|
||||
"host": "nsokolov-desktop",
|
||||
"id": "20c8acc0-f747-4d71-a389-46d078ebf047",
|
||||
"injected": false,
|
||||
"label": "mynet_0",
|
||||
"multi_host": false,
|
||||
"netmask": "255.255.255.248",
|
||||
"netmask_v6": null,
|
||||
"priority": null,
|
||||
"project_id": "1234",
|
||||
"rxtx_base": null,
|
||||
"updated_at": "2011-08-16 09:26:13.048257",
|
||||
"vlan": 100,
|
||||
"vpn_private_address": "10.0.0.2",
|
||||
"vpn_public_address": "127.0.0.1",
|
||||
"vpn_public_port": 1000
|
||||
},
|
||||
{
|
||||
"bridge": "br101",
|
||||
"bridge_interface": "eth0",
|
||||
"broadcast": "10.0.0.15",
|
||||
"cidr": "10.0.0.10/29",
|
||||
"cidr_v6": null,
|
||||
"created_at": "2011-08-15 06:19:19.885495",
|
||||
"deleted": false,
|
||||
"deleted_at": null,
|
||||
"dhcp_start": "10.0.0.11",
|
||||
"dns1": null,
|
||||
"dns2": null,
|
||||
"gateway": "10.0.0.9",
|
||||
"gateway_v6": null,
|
||||
"host": null,
|
||||
"id": "20c8acc0-f747-4d71-a389-46d078ebf000",
|
||||
"injected": false,
|
||||
"label": "mynet_1",
|
||||
"multi_host": false,
|
||||
"netmask": "255.255.255.248",
|
||||
"netmask_v6": null,
|
||||
"priority": null,
|
||||
"project_id": null,
|
||||
"rxtx_base": null,
|
||||
"updated_at": null,
|
||||
"vlan": 101,
|
||||
"vpn_private_address": "10.0.0.10",
|
||||
"vpn_public_address": null,
|
||||
"vpn_public_port": 1001
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
|
||||
// GetOutput is a sample response to a Get call.
|
||||
const GetOutput = `
|
||||
{
|
||||
"network": {
|
||||
"bridge": "br101",
|
||||
"bridge_interface": "eth0",
|
||||
"broadcast": "10.0.0.15",
|
||||
"cidr": "10.0.0.10/29",
|
||||
"cidr_v6": null,
|
||||
"created_at": "2011-08-15 06:19:19.885495",
|
||||
"deleted": false,
|
||||
"deleted_at": null,
|
||||
"dhcp_start": "10.0.0.11",
|
||||
"dns1": null,
|
||||
"dns2": null,
|
||||
"gateway": "10.0.0.9",
|
||||
"gateway_v6": null,
|
||||
"host": null,
|
||||
"id": "20c8acc0-f747-4d71-a389-46d078ebf000",
|
||||
"injected": false,
|
||||
"label": "mynet_1",
|
||||
"multi_host": false,
|
||||
"netmask": "255.255.255.248",
|
||||
"netmask_v6": null,
|
||||
"priority": null,
|
||||
"project_id": null,
|
||||
"rxtx_base": null,
|
||||
"updated_at": null,
|
||||
"vlan": 101,
|
||||
"vpn_private_address": "10.0.0.10",
|
||||
"vpn_public_address": null,
|
||||
"vpn_public_port": 1001
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
// FirstNetwork is the first result in ListOutput.
|
||||
var nilTime time.Time
|
||||
var FirstNetwork = Network{
|
||||
Bridge: "br100",
|
||||
BridgeInterface: "eth0",
|
||||
Broadcast: "10.0.0.7",
|
||||
CIDR: "10.0.0.0/29",
|
||||
CIDRv6: "",
|
||||
CreatedAt: time.Date(2011, 8, 15, 6, 19, 19, 387525000, time.UTC),
|
||||
Deleted: false,
|
||||
DeletedAt: nilTime,
|
||||
DHCPStart: "10.0.0.3",
|
||||
DNS1: "",
|
||||
DNS2: "",
|
||||
Gateway: "10.0.0.1",
|
||||
Gatewayv6: "",
|
||||
Host: "nsokolov-desktop",
|
||||
ID: "20c8acc0-f747-4d71-a389-46d078ebf047",
|
||||
Injected: false,
|
||||
Label: "mynet_0",
|
||||
MultiHost: false,
|
||||
Netmask: "255.255.255.248",
|
||||
Netmaskv6: "",
|
||||
Priority: 0,
|
||||
ProjectID: "1234",
|
||||
RXTXBase: 0,
|
||||
UpdatedAt: time.Date(2011, 8, 16, 9, 26, 13, 48257000, time.UTC),
|
||||
VLAN: 100,
|
||||
VPNPrivateAddress: "10.0.0.2",
|
||||
VPNPublicAddress: "127.0.0.1",
|
||||
VPNPublicPort: 1000,
|
||||
}
|
||||
|
||||
// SecondNetwork is the second result in ListOutput.
|
||||
var SecondNetwork = Network{
|
||||
Bridge: "br101",
|
||||
BridgeInterface: "eth0",
|
||||
Broadcast: "10.0.0.15",
|
||||
CIDR: "10.0.0.10/29",
|
||||
CIDRv6: "",
|
||||
CreatedAt: time.Date(2011, 8, 15, 6, 19, 19, 885495000, time.UTC),
|
||||
Deleted: false,
|
||||
DeletedAt: nilTime,
|
||||
DHCPStart: "10.0.0.11",
|
||||
DNS1: "",
|
||||
DNS2: "",
|
||||
Gateway: "10.0.0.9",
|
||||
Gatewayv6: "",
|
||||
Host: "",
|
||||
ID: "20c8acc0-f747-4d71-a389-46d078ebf000",
|
||||
Injected: false,
|
||||
Label: "mynet_1",
|
||||
MultiHost: false,
|
||||
Netmask: "255.255.255.248",
|
||||
Netmaskv6: "",
|
||||
Priority: 0,
|
||||
ProjectID: "",
|
||||
RXTXBase: 0,
|
||||
UpdatedAt: nilTime,
|
||||
VLAN: 101,
|
||||
VPNPrivateAddress: "10.0.0.10",
|
||||
VPNPublicAddress: "",
|
||||
VPNPublicPort: 1001,
|
||||
}
|
||||
|
||||
// ExpectedNetworkSlice is the slice of results that should be parsed
|
||||
// from ListOutput, in the expected order.
|
||||
var ExpectedNetworkSlice = []Network{FirstNetwork, SecondNetwork}
|
||||
|
||||
// HandleListSuccessfully configures the test server to respond to a List request.
|
||||
func HandleListSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/os-networks", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
fmt.Fprintf(w, ListOutput)
|
||||
})
|
||||
}
|
||||
|
||||
// HandleGetSuccessfully configures the test server to respond to a Get request
|
||||
// for an existing network.
|
||||
func HandleGetSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/os-networks/20c8acc0-f747-4d71-a389-46d078ebf000", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
fmt.Fprintf(w, GetOutput)
|
||||
})
|
||||
}
|
22
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/networks/requests.go
generated
vendored
Normal file
22
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/networks/requests.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
package networks
|
||||
|
||||
import (
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// List returns a Pager that allows you to iterate over a collection of Network.
|
||||
func List(client *gophercloud.ServiceClient) pagination.Pager {
|
||||
url := listURL(client)
|
||||
createPage := func(r pagination.PageResult) pagination.Page {
|
||||
return NetworkPage{pagination.SinglePageBase(r)}
|
||||
}
|
||||
return pagination.NewPager(client, url, createPage)
|
||||
}
|
||||
|
||||
// Get returns data about a previously created Network.
|
||||
func Get(client *gophercloud.ServiceClient, id string) GetResult {
|
||||
var res GetResult
|
||||
_, res.Err = client.Get(getURL(client, id), &res.Body, nil)
|
||||
return res
|
||||
}
|
222
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/networks/results.go
generated
vendored
Normal file
222
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/networks/results.go
generated
vendored
Normal file
@@ -0,0 +1,222 @@
|
||||
package networks
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// A Network represents a nova-network that an instance communicates on
|
||||
type Network struct {
|
||||
// The Bridge that VIFs on this network are connected to
|
||||
Bridge string `mapstructure:"bridge"`
|
||||
|
||||
// BridgeInterface is what interface is connected to the Bridge
|
||||
BridgeInterface string `mapstructure:"bridge_interface"`
|
||||
|
||||
// The Broadcast address of the network.
|
||||
Broadcast string `mapstructure:"broadcast"`
|
||||
|
||||
// CIDR is the IPv4 subnet.
|
||||
CIDR string `mapstructure:"cidr"`
|
||||
|
||||
// CIDRv6 is the IPv6 subnet.
|
||||
CIDRv6 string `mapstructure:"cidr_v6"`
|
||||
|
||||
// CreatedAt is when the network was created..
|
||||
CreatedAt time.Time `mapstructure:"-"`
|
||||
|
||||
// Deleted shows if the network has been deleted.
|
||||
Deleted bool `mapstructure:"deleted"`
|
||||
|
||||
// DeletedAt is the time when the network was deleted.
|
||||
DeletedAt time.Time `mapstructure:"-"`
|
||||
|
||||
// DHCPStart is the start of the DHCP address range.
|
||||
DHCPStart string `mapstructure:"dhcp_start"`
|
||||
|
||||
// DNS1 is the first DNS server to use through DHCP.
|
||||
DNS1 string `mapstructure:"dns_1"`
|
||||
|
||||
// DNS2 is the first DNS server to use through DHCP.
|
||||
DNS2 string `mapstructure:"dns_2"`
|
||||
|
||||
// Gateway is the network gateway.
|
||||
Gateway string `mapstructure:"gateway"`
|
||||
|
||||
// Gatewayv6 is the IPv6 network gateway.
|
||||
Gatewayv6 string `mapstructure:"gateway_v6"`
|
||||
|
||||
// Host is the host that the network service is running on.
|
||||
Host string `mapstructure:"host"`
|
||||
|
||||
// ID is the UUID of the network.
|
||||
ID string `mapstructure:"id"`
|
||||
|
||||
// Injected determines if network information is injected into the host.
|
||||
Injected bool `mapstructure:"injected"`
|
||||
|
||||
// Label is the common name that the network has..
|
||||
Label string `mapstructure:"label"`
|
||||
|
||||
// MultiHost is if multi-host networking is enablec..
|
||||
MultiHost bool `mapstructure:"multi_host"`
|
||||
|
||||
// Netmask is the network netmask.
|
||||
Netmask string `mapstructure:"netmask"`
|
||||
|
||||
// Netmaskv6 is the IPv6 netmask.
|
||||
Netmaskv6 string `mapstructure:"netmask_v6"`
|
||||
|
||||
// Priority is the network interface priority.
|
||||
Priority int `mapstructure:"priority"`
|
||||
|
||||
// ProjectID is the project associated with this network.
|
||||
ProjectID string `mapstructure:"project_id"`
|
||||
|
||||
// RXTXBase configures bandwidth entitlement.
|
||||
RXTXBase int `mapstructure:"rxtx_base"`
|
||||
|
||||
// UpdatedAt is the time when the network was last updated.
|
||||
UpdatedAt time.Time `mapstructure:"-"`
|
||||
|
||||
// VLAN is the vlan this network runs on.
|
||||
VLAN int `mapstructure:"vlan"`
|
||||
|
||||
// VPNPrivateAddress is the private address of the CloudPipe VPN.
|
||||
VPNPrivateAddress string `mapstructure:"vpn_private_address"`
|
||||
|
||||
// VPNPublicAddress is the public address of the CloudPipe VPN.
|
||||
VPNPublicAddress string `mapstructure:"vpn_public_address"`
|
||||
|
||||
// VPNPublicPort is the port of the CloudPipe VPN.
|
||||
VPNPublicPort int `mapstructure:"vpn_public_port"`
|
||||
}
|
||||
|
||||
// NetworkPage stores a single, only page of Networks
|
||||
// results from a List call.
|
||||
type NetworkPage struct {
|
||||
pagination.SinglePageBase
|
||||
}
|
||||
|
||||
// IsEmpty determines whether or not a NetworkPage is empty.
|
||||
func (page NetworkPage) IsEmpty() (bool, error) {
|
||||
va, err := ExtractNetworks(page)
|
||||
return len(va) == 0, err
|
||||
}
|
||||
|
||||
// ExtractNetworks interprets a page of results as a slice of Networks
|
||||
func ExtractNetworks(page pagination.Page) ([]Network, error) {
|
||||
var res struct {
|
||||
Networks []Network `mapstructure:"networks"`
|
||||
}
|
||||
|
||||
err := mapstructure.Decode(page.(NetworkPage).Body, &res)
|
||||
|
||||
var rawNetworks []interface{}
|
||||
body := page.(NetworkPage).Body
|
||||
switch body.(type) {
|
||||
case map[string]interface{}:
|
||||
rawNetworks = body.(map[string]interface{})["networks"].([]interface{})
|
||||
case map[string][]interface{}:
|
||||
rawNetworks = body.(map[string][]interface{})["networks"]
|
||||
default:
|
||||
return res.Networks, fmt.Errorf("Unknown type")
|
||||
}
|
||||
|
||||
for i := range rawNetworks {
|
||||
thisNetwork := rawNetworks[i].(map[string]interface{})
|
||||
if t, ok := thisNetwork["created_at"].(string); ok && t != "" {
|
||||
createdAt, err := time.Parse("2006-01-02 15:04:05.000000", t)
|
||||
if err != nil {
|
||||
return res.Networks, err
|
||||
}
|
||||
res.Networks[i].CreatedAt = createdAt
|
||||
}
|
||||
|
||||
if t, ok := thisNetwork["updated_at"].(string); ok && t != "" {
|
||||
updatedAt, err := time.Parse("2006-01-02 15:04:05.000000", t)
|
||||
if err != nil {
|
||||
return res.Networks, err
|
||||
}
|
||||
res.Networks[i].UpdatedAt = updatedAt
|
||||
}
|
||||
|
||||
if t, ok := thisNetwork["deleted_at"].(string); ok && t != "" {
|
||||
deletedAt, err := time.Parse("2006-01-02 15:04:05.000000", t)
|
||||
if err != nil {
|
||||
return res.Networks, err
|
||||
}
|
||||
res.Networks[i].DeletedAt = deletedAt
|
||||
}
|
||||
}
|
||||
|
||||
return res.Networks, err
|
||||
}
|
||||
|
||||
type NetworkResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// Extract is a method that attempts to interpret any Network resource
|
||||
// response as a Network struct.
|
||||
func (r NetworkResult) Extract() (*Network, error) {
|
||||
if r.Err != nil {
|
||||
return nil, r.Err
|
||||
}
|
||||
|
||||
var res struct {
|
||||
Network *Network `json:"network" mapstructure:"network"`
|
||||
}
|
||||
|
||||
config := &mapstructure.DecoderConfig{
|
||||
Result: &res,
|
||||
WeaklyTypedInput: true,
|
||||
}
|
||||
decoder, err := mapstructure.NewDecoder(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := decoder.Decode(r.Body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b := r.Body.(map[string]interface{})["network"].(map[string]interface{})
|
||||
|
||||
if t, ok := b["created_at"].(string); ok && t != "" {
|
||||
createdAt, err := time.Parse("2006-01-02 15:04:05.000000", t)
|
||||
if err != nil {
|
||||
return res.Network, err
|
||||
}
|
||||
res.Network.CreatedAt = createdAt
|
||||
}
|
||||
|
||||
if t, ok := b["updated_at"].(string); ok && t != "" {
|
||||
updatedAt, err := time.Parse("2006-01-02 15:04:05.000000", t)
|
||||
if err != nil {
|
||||
return res.Network, err
|
||||
}
|
||||
res.Network.UpdatedAt = updatedAt
|
||||
}
|
||||
|
||||
if t, ok := b["deleted_at"].(string); ok && t != "" {
|
||||
deletedAt, err := time.Parse("2006-01-02 15:04:05.000000", t)
|
||||
if err != nil {
|
||||
return res.Network, err
|
||||
}
|
||||
res.Network.DeletedAt = deletedAt
|
||||
}
|
||||
|
||||
return res.Network, err
|
||||
|
||||
}
|
||||
|
||||
// GetResult is the response from a Get operation. Call its Extract method to interpret it
|
||||
// as a Network.
|
||||
type GetResult struct {
|
||||
NetworkResult
|
||||
}
|
17
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/networks/urls.go
generated
vendored
Normal file
17
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/networks/urls.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
package networks
|
||||
|
||||
import "github.com/rackspace/gophercloud"
|
||||
|
||||
const resourcePath = "os-networks"
|
||||
|
||||
func resourceURL(c *gophercloud.ServiceClient) string {
|
||||
return c.ServiceURL(resourcePath)
|
||||
}
|
||||
|
||||
func listURL(c *gophercloud.ServiceClient) string {
|
||||
return resourceURL(c)
|
||||
}
|
||||
|
||||
func getURL(c *gophercloud.ServiceClient, id string) string {
|
||||
return c.ServiceURL(resourcePath, id)
|
||||
}
|
3
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/quotasets/doc.go
generated
vendored
Normal file
3
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/quotasets/doc.go
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
// Package quotasets provides information and interaction with QuotaSet
|
||||
// extension for the OpenStack Compute service.
|
||||
package quotasets
|
59
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/quotasets/fixtures.go
generated
vendored
Normal file
59
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/quotasets/fixtures.go
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
// +build fixtures
|
||||
|
||||
package quotasets
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
"github.com/rackspace/gophercloud/testhelper/client"
|
||||
)
|
||||
|
||||
// GetOutput is a sample response to a Get call.
|
||||
const GetOutput = `
|
||||
{
|
||||
"quota_set" : {
|
||||
"instances" : 25,
|
||||
"security_groups" : 10,
|
||||
"security_group_rules" : 20,
|
||||
"cores" : 200,
|
||||
"injected_file_content_bytes" : 10240,
|
||||
"injected_files" : 5,
|
||||
"metadata_items" : 128,
|
||||
"ram" : 200000,
|
||||
"keypairs" : 10,
|
||||
"injected_file_path_bytes" : 255
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const FirstTenantID = "555544443333222211110000ffffeeee"
|
||||
|
||||
// FirstQuotaSet is the first result in ListOutput.
|
||||
var FirstQuotaSet = QuotaSet{
|
||||
FixedIps: 0,
|
||||
FloatingIps: 0,
|
||||
InjectedFileContentBytes: 10240,
|
||||
InjectedFilePathBytes: 255,
|
||||
InjectedFiles: 5,
|
||||
KeyPairs: 10,
|
||||
MetadataItems: 128,
|
||||
Ram: 200000,
|
||||
SecurityGroupRules: 20,
|
||||
SecurityGroups: 10,
|
||||
Cores: 200,
|
||||
Instances: 25,
|
||||
}
|
||||
|
||||
// HandleGetSuccessfully configures the test server to respond to a Get request for sample tenant
|
||||
func HandleGetSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/os-quota-sets/"+FirstTenantID, func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
fmt.Fprintf(w, GetOutput)
|
||||
})
|
||||
}
|
12
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/quotasets/requests.go
generated
vendored
Normal file
12
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/quotasets/requests.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
package quotasets
|
||||
|
||||
import (
|
||||
"github.com/rackspace/gophercloud"
|
||||
)
|
||||
|
||||
// Get returns public data about a previously created QuotaSet.
|
||||
func Get(client *gophercloud.ServiceClient, tenantID string) GetResult {
|
||||
var res GetResult
|
||||
_, res.Err = client.Get(getURL(client, tenantID), &res.Body, nil)
|
||||
return res
|
||||
}
|
86
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/quotasets/results.go
generated
vendored
Normal file
86
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/quotasets/results.go
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
package quotasets
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// QuotaSet is a set of operational limits that allow for control of compute usage.
|
||||
type QuotaSet struct {
|
||||
//ID is tenant associated with this quota_set
|
||||
ID string `mapstructure:"id"`
|
||||
//FixedIps is number of fixed ips alloted this quota_set
|
||||
FixedIps int `mapstructure:"fixed_ips"`
|
||||
// FloatingIps is number of floating ips alloted this quota_set
|
||||
FloatingIps int `mapstructure:"floating_ips"`
|
||||
// InjectedFileContentBytes is content bytes allowed for each injected file
|
||||
InjectedFileContentBytes int `mapstructure:"injected_file_content_bytes"`
|
||||
// InjectedFilePathBytes is allowed bytes for each injected file path
|
||||
InjectedFilePathBytes int `mapstructure:"injected_file_path_bytes"`
|
||||
// InjectedFiles is injected files allowed for each project
|
||||
InjectedFiles int `mapstructure:"injected_files"`
|
||||
// KeyPairs is number of ssh keypairs
|
||||
KeyPairs int `mapstructure:"keypairs"`
|
||||
// MetadataItems is number of metadata items allowed for each instance
|
||||
MetadataItems int `mapstructure:"metadata_items"`
|
||||
// Ram is megabytes allowed for each instance
|
||||
Ram int `mapstructure:"ram"`
|
||||
// SecurityGroupRules is rules allowed for each security group
|
||||
SecurityGroupRules int `mapstructure:"security_group_rules"`
|
||||
// SecurityGroups security groups allowed for each project
|
||||
SecurityGroups int `mapstructure:"security_groups"`
|
||||
// Cores is number of instance cores allowed for each project
|
||||
Cores int `mapstructure:"cores"`
|
||||
// Instances is number of instances allowed for each project
|
||||
Instances int `mapstructure:"instances"`
|
||||
}
|
||||
|
||||
// QuotaSetPage stores a single, only page of QuotaSet results from a List call.
|
||||
type QuotaSetPage struct {
|
||||
pagination.SinglePageBase
|
||||
}
|
||||
|
||||
// IsEmpty determines whether or not a QuotaSetsetPage is empty.
|
||||
func (page QuotaSetPage) IsEmpty() (bool, error) {
|
||||
ks, err := ExtractQuotaSets(page)
|
||||
return len(ks) == 0, err
|
||||
}
|
||||
|
||||
// ExtractQuotaSets interprets a page of results as a slice of QuotaSets.
|
||||
func ExtractQuotaSets(page pagination.Page) ([]QuotaSet, error) {
|
||||
var resp struct {
|
||||
QuotaSets []QuotaSet `mapstructure:"quotas"`
|
||||
}
|
||||
|
||||
err := mapstructure.Decode(page.(QuotaSetPage).Body, &resp)
|
||||
results := make([]QuotaSet, len(resp.QuotaSets))
|
||||
for i, q := range resp.QuotaSets {
|
||||
results[i] = q
|
||||
}
|
||||
return results, err
|
||||
}
|
||||
|
||||
type quotaResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// Extract is a method that attempts to interpret any QuotaSet resource response as a QuotaSet struct.
|
||||
func (r quotaResult) Extract() (*QuotaSet, error) {
|
||||
if r.Err != nil {
|
||||
return nil, r.Err
|
||||
}
|
||||
|
||||
var res struct {
|
||||
QuotaSet *QuotaSet `json:"quota_set" mapstructure:"quota_set"`
|
||||
}
|
||||
|
||||
err := mapstructure.Decode(r.Body, &res)
|
||||
return res.QuotaSet, err
|
||||
}
|
||||
|
||||
// GetResult is the response from a Get operation. Call its Extract method to interpret it
|
||||
// as a QuotaSet.
|
||||
type GetResult struct {
|
||||
quotaResult
|
||||
}
|
13
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/quotasets/urls.go
generated
vendored
Normal file
13
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/quotasets/urls.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
package quotasets
|
||||
|
||||
import "github.com/rackspace/gophercloud"
|
||||
|
||||
const resourcePath = "os-quota-sets"
|
||||
|
||||
func resourceURL(c *gophercloud.ServiceClient) string {
|
||||
return c.ServiceURL(resourcePath)
|
||||
}
|
||||
|
||||
func getURL(c *gophercloud.ServiceClient, tenantID string) string {
|
||||
return c.ServiceURL(resourcePath, tenantID)
|
||||
}
|
3
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/schedulerhints/doc.go
generated
vendored
Normal file
3
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/schedulerhints/doc.go
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
// Package schedulerhints enables instances to provide the OpenStack scheduler
|
||||
// hints about where they should be placed in the cloud.
|
||||
package schedulerhints
|
134
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/schedulerhints/requests.go
generated
vendored
Normal file
134
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/schedulerhints/requests.go
generated
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
package schedulerhints
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
||||
)
|
||||
|
||||
// SchedulerHints represents a set of scheduling hints that are passed to the
|
||||
// OpenStack scheduler
|
||||
type SchedulerHints struct {
|
||||
// Group specifies a Server Group to place the instance in.
|
||||
Group string
|
||||
|
||||
// DifferentHost will place the instance on a compute node that does not
|
||||
// host the given instances.
|
||||
DifferentHost []string
|
||||
|
||||
// SameHost will place the instance on a compute node that hosts the given
|
||||
// instances.
|
||||
SameHost []string
|
||||
|
||||
// Query is a conditional statement that results in compute nodes able to
|
||||
// host the instance.
|
||||
Query []interface{}
|
||||
|
||||
// TargetCell specifies a cell name where the instance will be placed.
|
||||
TargetCell string
|
||||
|
||||
// BuildNearHostIP specifies a subnet of compute nodes to host the instance.
|
||||
BuildNearHostIP string
|
||||
}
|
||||
|
||||
// SchedulerHintsBuilder builds the scheduler hints into a serializable format.
|
||||
type SchedulerHintsBuilder interface {
|
||||
ToServerSchedulerHintsMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// ToServerSchedulerHintsMap builds the scheduler hints into a serializable format.
|
||||
func (opts SchedulerHints) ToServerSchedulerHintsMap() (map[string]interface{}, error) {
|
||||
sh := make(map[string]interface{})
|
||||
|
||||
uuidRegex, _ := regexp.Compile("^[a-z0-9]{8}-[a-z0-9]{4}-[1-5][a-z0-9]{3}-[a-z0-9]{4}-[a-z0-9]{12}$")
|
||||
|
||||
if opts.Group != "" {
|
||||
if !uuidRegex.MatchString(opts.Group) {
|
||||
return nil, fmt.Errorf("Group must be a UUID")
|
||||
}
|
||||
sh["group"] = opts.Group
|
||||
}
|
||||
|
||||
if len(opts.DifferentHost) > 0 {
|
||||
for _, diffHost := range opts.DifferentHost {
|
||||
if !uuidRegex.MatchString(diffHost) {
|
||||
return nil, fmt.Errorf("The hosts in DifferentHost must be in UUID format.")
|
||||
}
|
||||
}
|
||||
sh["different_host"] = opts.DifferentHost
|
||||
}
|
||||
|
||||
if len(opts.SameHost) > 0 {
|
||||
for _, sameHost := range opts.SameHost {
|
||||
if !uuidRegex.MatchString(sameHost) {
|
||||
return nil, fmt.Errorf("The hosts in SameHost must be in UUID format.")
|
||||
}
|
||||
}
|
||||
sh["same_host"] = opts.SameHost
|
||||
}
|
||||
|
||||
/* Query can be something simple like:
|
||||
[">=", "$free_ram_mb", 1024]
|
||||
|
||||
Or more complex like:
|
||||
['and',
|
||||
['>=', '$free_ram_mb', 1024],
|
||||
['>=', '$free_disk_mb', 200 * 1024]
|
||||
]
|
||||
|
||||
Because of the possible complexity, just make sure the length is a minimum of 3.
|
||||
*/
|
||||
if len(opts.Query) > 0 {
|
||||
if len(opts.Query) < 3 {
|
||||
return nil, fmt.Errorf("Query must be a conditional statement in the format of [op,variable,value]")
|
||||
}
|
||||
sh["query"] = opts.Query
|
||||
}
|
||||
|
||||
if opts.TargetCell != "" {
|
||||
sh["target_cell"] = opts.TargetCell
|
||||
}
|
||||
|
||||
if opts.BuildNearHostIP != "" {
|
||||
if _, _, err := net.ParseCIDR(opts.BuildNearHostIP); err != nil {
|
||||
return nil, fmt.Errorf("BuildNearHostIP must be a valid subnet in the form 192.168.1.1/24")
|
||||
}
|
||||
ipParts := strings.Split(opts.BuildNearHostIP, "/")
|
||||
sh["build_near_host_ip"] = ipParts[0]
|
||||
sh["cidr"] = "/" + ipParts[1]
|
||||
}
|
||||
|
||||
return sh, nil
|
||||
}
|
||||
|
||||
// CreateOptsExt adds a SchedulerHints option to the base CreateOpts.
|
||||
type CreateOptsExt struct {
|
||||
servers.CreateOptsBuilder
|
||||
|
||||
// SchedulerHints provides a set of hints to the scheduler.
|
||||
SchedulerHints SchedulerHintsBuilder
|
||||
}
|
||||
|
||||
// ToServerCreateMap adds the SchedulerHints option 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
|
||||
}
|
||||
|
||||
schedulerHints, err := opts.SchedulerHints.ToServerSchedulerHintsMap()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(schedulerHints) == 0 {
|
||||
return base, nil
|
||||
}
|
||||
|
||||
base["os:scheduler_hints"] = schedulerHints
|
||||
|
||||
return base, nil
|
||||
}
|
1
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups/doc.go
generated
vendored
Normal file
1
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups/doc.go
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
package secgroups
|
303
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups/fixtures.go
generated
vendored
Normal file
303
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups/fixtures.go
generated
vendored
Normal file
@@ -0,0 +1,303 @@
|
||||
package secgroups
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
fake "github.com/rackspace/gophercloud/testhelper/client"
|
||||
)
|
||||
|
||||
const rootPath = "/os-security-groups"
|
||||
|
||||
const listGroupsJSON = `
|
||||
{
|
||||
"security_groups": [
|
||||
{
|
||||
"description": "default",
|
||||
"id": "{groupID}",
|
||||
"name": "default",
|
||||
"rules": [],
|
||||
"tenant_id": "openstack"
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
|
||||
func mockListGroupsResponse(t *testing.T) {
|
||||
th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, listGroupsJSON)
|
||||
})
|
||||
}
|
||||
|
||||
func mockListGroupsByServerResponse(t *testing.T, serverID string) {
|
||||
url := fmt.Sprintf("/servers/%s%s", serverID, rootPath)
|
||||
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, listGroupsJSON)
|
||||
})
|
||||
}
|
||||
|
||||
func mockCreateGroupResponse(t *testing.T) {
|
||||
th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"security_group": {
|
||||
"name": "test",
|
||||
"description": "something"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"security_group": {
|
||||
"description": "something",
|
||||
"id": "{groupID}",
|
||||
"name": "test",
|
||||
"rules": [],
|
||||
"tenant_id": "openstack"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
}
|
||||
|
||||
func mockUpdateGroupResponse(t *testing.T, groupID string) {
|
||||
url := fmt.Sprintf("%s/%s", rootPath, groupID)
|
||||
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "PUT")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"security_group": {
|
||||
"name": "new_name",
|
||||
"description": "new_desc"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"security_group": {
|
||||
"description": "something",
|
||||
"id": "{groupID}",
|
||||
"name": "new_name",
|
||||
"rules": [],
|
||||
"tenant_id": "openstack"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
}
|
||||
|
||||
func mockGetGroupsResponse(t *testing.T, groupID string) {
|
||||
url := fmt.Sprintf("%s/%s", rootPath, groupID)
|
||||
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"security_group": {
|
||||
"description": "default",
|
||||
"id": "{groupID}",
|
||||
"name": "default",
|
||||
"rules": [
|
||||
{
|
||||
"from_port": 80,
|
||||
"group": {
|
||||
"tenant_id": "openstack",
|
||||
"name": "default"
|
||||
},
|
||||
"ip_protocol": "TCP",
|
||||
"to_port": 85,
|
||||
"parent_group_id": "{groupID}",
|
||||
"ip_range": {
|
||||
"cidr": "0.0.0.0"
|
||||
},
|
||||
"id": "{ruleID}"
|
||||
}
|
||||
],
|
||||
"tenant_id": "openstack"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
}
|
||||
|
||||
func mockGetNumericIDGroupResponse(t *testing.T, groupID int) {
|
||||
url := fmt.Sprintf("%s/%d", rootPath, groupID)
|
||||
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"security_group": {
|
||||
"id": 12345
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
}
|
||||
|
||||
func mockDeleteGroupResponse(t *testing.T, groupID string) {
|
||||
url := fmt.Sprintf("%s/%s", rootPath, groupID)
|
||||
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "DELETE")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
})
|
||||
}
|
||||
|
||||
func mockAddRuleResponse(t *testing.T) {
|
||||
th.Mux.HandleFunc("/os-security-group-rules", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"security_group_rule": {
|
||||
"from_port": 22,
|
||||
"ip_protocol": "TCP",
|
||||
"to_port": 22,
|
||||
"parent_group_id": "{groupID}",
|
||||
"cidr": "0.0.0.0/0"
|
||||
}
|
||||
} `)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"security_group_rule": {
|
||||
"from_port": 22,
|
||||
"group": {},
|
||||
"ip_protocol": "TCP",
|
||||
"to_port": 22,
|
||||
"parent_group_id": "{groupID}",
|
||||
"ip_range": {
|
||||
"cidr": "0.0.0.0/0"
|
||||
},
|
||||
"id": "{ruleID}"
|
||||
}
|
||||
}`)
|
||||
})
|
||||
}
|
||||
|
||||
func mockAddRuleResponseICMPZero(t *testing.T) {
|
||||
th.Mux.HandleFunc("/os-security-group-rules", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"security_group_rule": {
|
||||
"from_port": 0,
|
||||
"ip_protocol": "ICMP",
|
||||
"to_port": 0,
|
||||
"parent_group_id": "{groupID}",
|
||||
"cidr": "0.0.0.0/0"
|
||||
}
|
||||
} `)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"security_group_rule": {
|
||||
"from_port": 0,
|
||||
"group": {},
|
||||
"ip_protocol": "ICMP",
|
||||
"to_port": 0,
|
||||
"parent_group_id": "{groupID}",
|
||||
"ip_range": {
|
||||
"cidr": "0.0.0.0/0"
|
||||
},
|
||||
"id": "{ruleID}"
|
||||
}
|
||||
}`)
|
||||
})
|
||||
}
|
||||
|
||||
func mockDeleteRuleResponse(t *testing.T, ruleID string) {
|
||||
url := fmt.Sprintf("/os-security-group-rules/%s", ruleID)
|
||||
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "DELETE")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
})
|
||||
}
|
||||
|
||||
func mockAddServerToGroupResponse(t *testing.T, serverID string) {
|
||||
url := fmt.Sprintf("/servers/%s/action", serverID)
|
||||
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"addSecurityGroup": {
|
||||
"name": "test"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
fmt.Fprintf(w, `{}`)
|
||||
})
|
||||
}
|
||||
|
||||
func mockRemoveServerFromGroupResponse(t *testing.T, serverID string) {
|
||||
url := fmt.Sprintf("/servers/%s/action", serverID)
|
||||
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"removeSecurityGroup": {
|
||||
"name": "test"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
fmt.Fprintf(w, `{}`)
|
||||
})
|
||||
}
|
258
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups/requests.go
generated
vendored
Normal file
258
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups/requests.go
generated
vendored
Normal file
@@ -0,0 +1,258 @@
|
||||
package secgroups
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
func commonList(client *gophercloud.ServiceClient, url string) pagination.Pager {
|
||||
createPage := func(r pagination.PageResult) pagination.Page {
|
||||
return SecurityGroupPage{pagination.SinglePageBase(r)}
|
||||
}
|
||||
|
||||
return pagination.NewPager(client, url, createPage)
|
||||
}
|
||||
|
||||
// List will return a collection of all the security groups for a particular
|
||||
// tenant.
|
||||
func List(client *gophercloud.ServiceClient) pagination.Pager {
|
||||
return commonList(client, rootURL(client))
|
||||
}
|
||||
|
||||
// ListByServer will return a collection of all the security groups which are
|
||||
// associated with a particular server.
|
||||
func ListByServer(client *gophercloud.ServiceClient, serverID string) pagination.Pager {
|
||||
return commonList(client, listByServerURL(client, serverID))
|
||||
}
|
||||
|
||||
// GroupOpts is the underlying struct responsible for creating or updating
|
||||
// security groups. It therefore represents the mutable attributes of a
|
||||
// security group.
|
||||
type GroupOpts struct {
|
||||
// Required - the name of your security group.
|
||||
Name string `json:"name"`
|
||||
|
||||
// Required - the description of your security group.
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
// CreateOpts is the struct responsible for creating a security group.
|
||||
type CreateOpts GroupOpts
|
||||
|
||||
// CreateOptsBuilder builds the create options into a serializable format.
|
||||
type CreateOptsBuilder interface {
|
||||
ToSecGroupCreateMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
var (
|
||||
errName = errors.New("Name is a required field")
|
||||
errDesc = errors.New("Description is a required field")
|
||||
)
|
||||
|
||||
// ToSecGroupCreateMap builds the create options into a serializable format.
|
||||
func (opts CreateOpts) ToSecGroupCreateMap() (map[string]interface{}, error) {
|
||||
sg := make(map[string]interface{})
|
||||
|
||||
if opts.Name == "" {
|
||||
return sg, errName
|
||||
}
|
||||
if opts.Description == "" {
|
||||
return sg, errDesc
|
||||
}
|
||||
|
||||
sg["name"] = opts.Name
|
||||
sg["description"] = opts.Description
|
||||
|
||||
return map[string]interface{}{"security_group": sg}, nil
|
||||
}
|
||||
|
||||
// Create will create a new security group.
|
||||
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
|
||||
var result CreateResult
|
||||
|
||||
reqBody, err := opts.ToSecGroupCreateMap()
|
||||
if err != nil {
|
||||
result.Err = err
|
||||
return result
|
||||
}
|
||||
|
||||
_, result.Err = client.Post(rootURL(client), reqBody, &result.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200},
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// UpdateOpts is the struct responsible for updating an existing security group.
|
||||
type UpdateOpts GroupOpts
|
||||
|
||||
// UpdateOptsBuilder builds the update options into a serializable format.
|
||||
type UpdateOptsBuilder interface {
|
||||
ToSecGroupUpdateMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// ToSecGroupUpdateMap builds the update options into a serializable format.
|
||||
func (opts UpdateOpts) ToSecGroupUpdateMap() (map[string]interface{}, error) {
|
||||
sg := make(map[string]interface{})
|
||||
|
||||
if opts.Name == "" {
|
||||
return sg, errName
|
||||
}
|
||||
if opts.Description == "" {
|
||||
return sg, errDesc
|
||||
}
|
||||
|
||||
sg["name"] = opts.Name
|
||||
sg["description"] = opts.Description
|
||||
|
||||
return map[string]interface{}{"security_group": sg}, nil
|
||||
}
|
||||
|
||||
// Update will modify the mutable properties of a security group, notably its
|
||||
// name and description.
|
||||
func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
|
||||
var result UpdateResult
|
||||
|
||||
reqBody, err := opts.ToSecGroupUpdateMap()
|
||||
if err != nil {
|
||||
result.Err = err
|
||||
return result
|
||||
}
|
||||
|
||||
_, result.Err = client.Put(resourceURL(client, id), reqBody, &result.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200},
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Get will return details for a particular security group.
|
||||
func Get(client *gophercloud.ServiceClient, id string) GetResult {
|
||||
var result GetResult
|
||||
_, result.Err = client.Get(resourceURL(client, id), &result.Body, nil)
|
||||
return result
|
||||
}
|
||||
|
||||
// Delete will permanently delete a security group from the project.
|
||||
func Delete(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult {
|
||||
var result gophercloud.ErrResult
|
||||
_, result.Err = client.Delete(resourceURL(client, id), nil)
|
||||
return result
|
||||
}
|
||||
|
||||
// CreateRuleOpts represents the configuration for adding a new rule to an
|
||||
// existing security group.
|
||||
type CreateRuleOpts struct {
|
||||
// Required - the ID of the group that this rule will be added to.
|
||||
ParentGroupID string `json:"parent_group_id"`
|
||||
|
||||
// Required - the lower bound of the port range that will be opened.
|
||||
FromPort int `json:"from_port"`
|
||||
|
||||
// Required - the upper bound of the port range that will be opened.
|
||||
ToPort int `json:"to_port"`
|
||||
|
||||
// Required - the protocol type that will be allowed, e.g. TCP.
|
||||
IPProtocol string `json:"ip_protocol"`
|
||||
|
||||
// ONLY required if FromGroupID is blank. This represents the IP range that
|
||||
// will be the source of network traffic to your security group. Use
|
||||
// 0.0.0.0/0 to allow all IP addresses.
|
||||
CIDR string `json:"cidr,omitempty"`
|
||||
|
||||
// ONLY required if CIDR is blank. This value represents the ID of a group
|
||||
// that forwards traffic to the parent group. So, instead of accepting
|
||||
// network traffic from an entire IP range, you can instead refine the
|
||||
// inbound source by an existing security group.
|
||||
FromGroupID string `json:"group_id,omitempty"`
|
||||
}
|
||||
|
||||
// CreateRuleOptsBuilder builds the create rule options into a serializable format.
|
||||
type CreateRuleOptsBuilder interface {
|
||||
ToRuleCreateMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// ToRuleCreateMap builds the create rule options into a serializable format.
|
||||
func (opts CreateRuleOpts) ToRuleCreateMap() (map[string]interface{}, error) {
|
||||
rule := make(map[string]interface{})
|
||||
|
||||
if opts.ParentGroupID == "" {
|
||||
return rule, errors.New("A ParentGroupID must be set")
|
||||
}
|
||||
if opts.FromPort == 0 && strings.ToUpper(opts.IPProtocol) != "ICMP" {
|
||||
return rule, errors.New("A FromPort must be set")
|
||||
}
|
||||
if opts.ToPort == 0 && strings.ToUpper(opts.IPProtocol) != "ICMP" {
|
||||
return rule, errors.New("A ToPort must be set")
|
||||
}
|
||||
if opts.IPProtocol == "" {
|
||||
return rule, errors.New("A IPProtocol must be set")
|
||||
}
|
||||
if opts.CIDR == "" && opts.FromGroupID == "" {
|
||||
return rule, errors.New("A CIDR or FromGroupID must be set")
|
||||
}
|
||||
|
||||
rule["parent_group_id"] = opts.ParentGroupID
|
||||
rule["from_port"] = opts.FromPort
|
||||
rule["to_port"] = opts.ToPort
|
||||
rule["ip_protocol"] = opts.IPProtocol
|
||||
|
||||
if opts.CIDR != "" {
|
||||
rule["cidr"] = opts.CIDR
|
||||
}
|
||||
if opts.FromGroupID != "" {
|
||||
rule["group_id"] = opts.FromGroupID
|
||||
}
|
||||
|
||||
return map[string]interface{}{"security_group_rule": rule}, nil
|
||||
}
|
||||
|
||||
// CreateRule will add a new rule to an existing security group (whose ID is
|
||||
// specified in CreateRuleOpts). You have the option of controlling inbound
|
||||
// traffic from either an IP range (CIDR) or from another security group.
|
||||
func CreateRule(client *gophercloud.ServiceClient, opts CreateRuleOptsBuilder) CreateRuleResult {
|
||||
var result CreateRuleResult
|
||||
|
||||
reqBody, err := opts.ToRuleCreateMap()
|
||||
if err != nil {
|
||||
result.Err = err
|
||||
return result
|
||||
}
|
||||
|
||||
_, result.Err = client.Post(rootRuleURL(client), reqBody, &result.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200},
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// DeleteRule will permanently delete a rule from a security group.
|
||||
func DeleteRule(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult {
|
||||
var result gophercloud.ErrResult
|
||||
_, result.Err = client.Delete(resourceRuleURL(client, id), nil)
|
||||
return result
|
||||
}
|
||||
|
||||
func actionMap(prefix, groupName string) map[string]map[string]string {
|
||||
return map[string]map[string]string{
|
||||
prefix + "SecurityGroup": map[string]string{"name": groupName},
|
||||
}
|
||||
}
|
||||
|
||||
// AddServerToGroup will associate a server and a security group, enforcing the
|
||||
// rules of the group on the server.
|
||||
func AddServerToGroup(client *gophercloud.ServiceClient, serverID, groupName string) gophercloud.ErrResult {
|
||||
var result gophercloud.ErrResult
|
||||
_, result.Err = client.Post(serverActionURL(client, serverID), actionMap("add", groupName), &result.Body, nil)
|
||||
return result
|
||||
}
|
||||
|
||||
// RemoveServerFromGroup will disassociate a server from a security group.
|
||||
func RemoveServerFromGroup(client *gophercloud.ServiceClient, serverID, groupName string) gophercloud.ErrResult {
|
||||
var result gophercloud.ErrResult
|
||||
_, result.Err = client.Post(serverActionURL(client, serverID), actionMap("remove", groupName), &result.Body, nil)
|
||||
return result
|
||||
}
|
147
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups/results.go
generated
vendored
Normal file
147
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups/results.go
generated
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
package secgroups
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/mapstructure"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// SecurityGroup represents a security group.
|
||||
type SecurityGroup struct {
|
||||
// The unique ID of the group. If Neutron is installed, this ID will be
|
||||
// represented as a string UUID; if Neutron is not installed, it will be a
|
||||
// numeric ID. For the sake of consistency, we always cast it to a string.
|
||||
ID string
|
||||
|
||||
// The human-readable name of the group, which needs to be unique.
|
||||
Name string
|
||||
|
||||
// The human-readable description of the group.
|
||||
Description string
|
||||
|
||||
// The rules which determine how this security group operates.
|
||||
Rules []Rule
|
||||
|
||||
// The ID of the tenant to which this security group belongs.
|
||||
TenantID string `mapstructure:"tenant_id"`
|
||||
}
|
||||
|
||||
// Rule represents a security group rule, a policy which determines how a
|
||||
// security group operates and what inbound traffic it allows in.
|
||||
type Rule struct {
|
||||
// The unique ID. If Neutron is installed, this ID will be
|
||||
// represented as a string UUID; if Neutron is not installed, it will be a
|
||||
// numeric ID. For the sake of consistency, we always cast it to a string.
|
||||
ID string
|
||||
|
||||
// The lower bound of the port range which this security group should open up
|
||||
FromPort int `mapstructure:"from_port"`
|
||||
|
||||
// The upper bound of the port range which this security group should open up
|
||||
ToPort int `mapstructure:"to_port"`
|
||||
|
||||
// The IP protocol (e.g. TCP) which the security group accepts
|
||||
IPProtocol string `mapstructure:"ip_protocol"`
|
||||
|
||||
// The CIDR IP range whose traffic can be received
|
||||
IPRange IPRange `mapstructure:"ip_range"`
|
||||
|
||||
// The security group ID to which this rule belongs
|
||||
ParentGroupID string `mapstructure:"parent_group_id"`
|
||||
|
||||
// Not documented.
|
||||
Group Group
|
||||
}
|
||||
|
||||
// IPRange represents the IP range whose traffic will be accepted by the
|
||||
// security group.
|
||||
type IPRange struct {
|
||||
CIDR string
|
||||
}
|
||||
|
||||
// Group represents a group.
|
||||
type Group struct {
|
||||
TenantID string `mapstructure:"tenant_id"`
|
||||
Name string
|
||||
}
|
||||
|
||||
// SecurityGroupPage is a single page of a SecurityGroup collection.
|
||||
type SecurityGroupPage struct {
|
||||
pagination.SinglePageBase
|
||||
}
|
||||
|
||||
// IsEmpty determines whether or not a page of Security Groups contains any results.
|
||||
func (page SecurityGroupPage) IsEmpty() (bool, error) {
|
||||
users, err := ExtractSecurityGroups(page)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return len(users) == 0, nil
|
||||
}
|
||||
|
||||
// ExtractSecurityGroups returns a slice of SecurityGroups contained in a single page of results.
|
||||
func ExtractSecurityGroups(page pagination.Page) ([]SecurityGroup, error) {
|
||||
casted := page.(SecurityGroupPage).Body
|
||||
var response struct {
|
||||
SecurityGroups []SecurityGroup `mapstructure:"security_groups"`
|
||||
}
|
||||
|
||||
err := mapstructure.WeakDecode(casted, &response)
|
||||
|
||||
return response.SecurityGroups, err
|
||||
}
|
||||
|
||||
type commonResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// CreateResult represents the result of a create operation.
|
||||
type CreateResult struct {
|
||||
commonResult
|
||||
}
|
||||
|
||||
// GetResult represents the result of a get operation.
|
||||
type GetResult struct {
|
||||
commonResult
|
||||
}
|
||||
|
||||
// UpdateResult represents the result of an update operation.
|
||||
type UpdateResult struct {
|
||||
commonResult
|
||||
}
|
||||
|
||||
// Extract will extract a SecurityGroup struct from most responses.
|
||||
func (r commonResult) Extract() (*SecurityGroup, error) {
|
||||
if r.Err != nil {
|
||||
return nil, r.Err
|
||||
}
|
||||
|
||||
var response struct {
|
||||
SecurityGroup SecurityGroup `mapstructure:"security_group"`
|
||||
}
|
||||
|
||||
err := mapstructure.WeakDecode(r.Body, &response)
|
||||
|
||||
return &response.SecurityGroup, err
|
||||
}
|
||||
|
||||
// CreateRuleResult represents the result when adding rules to a security group.
|
||||
type CreateRuleResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// Extract will extract a Rule struct from a CreateRuleResult.
|
||||
func (r CreateRuleResult) Extract() (*Rule, error) {
|
||||
if r.Err != nil {
|
||||
return nil, r.Err
|
||||
}
|
||||
|
||||
var response struct {
|
||||
Rule Rule `mapstructure:"security_group_rule"`
|
||||
}
|
||||
|
||||
err := mapstructure.WeakDecode(r.Body, &response)
|
||||
|
||||
return &response.Rule, err
|
||||
}
|
32
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups/urls.go
generated
vendored
Normal file
32
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups/urls.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
package secgroups
|
||||
|
||||
import "github.com/rackspace/gophercloud"
|
||||
|
||||
const (
|
||||
secgrouppath = "os-security-groups"
|
||||
rulepath = "os-security-group-rules"
|
||||
)
|
||||
|
||||
func resourceURL(c *gophercloud.ServiceClient, id string) string {
|
||||
return c.ServiceURL(secgrouppath, id)
|
||||
}
|
||||
|
||||
func rootURL(c *gophercloud.ServiceClient) string {
|
||||
return c.ServiceURL(secgrouppath)
|
||||
}
|
||||
|
||||
func listByServerURL(c *gophercloud.ServiceClient, serverID string) string {
|
||||
return c.ServiceURL("servers", serverID, secgrouppath)
|
||||
}
|
||||
|
||||
func rootRuleURL(c *gophercloud.ServiceClient) string {
|
||||
return c.ServiceURL(rulepath)
|
||||
}
|
||||
|
||||
func resourceRuleURL(c *gophercloud.ServiceClient, id string) string {
|
||||
return c.ServiceURL(rulepath, id)
|
||||
}
|
||||
|
||||
func serverActionURL(c *gophercloud.ServiceClient, id string) string {
|
||||
return c.ServiceURL("servers", id, "action")
|
||||
}
|
2
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/servergroups/doc.go
generated
vendored
Normal file
2
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/servergroups/doc.go
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
// Package servergroups provides the ability to manage server groups
|
||||
package servergroups
|
161
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/servergroups/fixtures.go
generated
vendored
Normal file
161
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/servergroups/fixtures.go
generated
vendored
Normal file
@@ -0,0 +1,161 @@
|
||||
// +build fixtures
|
||||
|
||||
package servergroups
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
"github.com/rackspace/gophercloud/testhelper/client"
|
||||
)
|
||||
|
||||
// ListOutput is a sample response to a List call.
|
||||
const ListOutput = `
|
||||
{
|
||||
"server_groups": [
|
||||
{
|
||||
"id": "616fb98f-46ca-475e-917e-2563e5a8cd19",
|
||||
"name": "test",
|
||||
"policies": [
|
||||
"anti-affinity"
|
||||
],
|
||||
"members": [],
|
||||
"metadata": {}
|
||||
},
|
||||
{
|
||||
"id": "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
|
||||
"name": "test2",
|
||||
"policies": [
|
||||
"affinity"
|
||||
],
|
||||
"members": [],
|
||||
"metadata": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
|
||||
// GetOutput is a sample response to a Get call.
|
||||
const GetOutput = `
|
||||
{
|
||||
"server_group": {
|
||||
"id": "616fb98f-46ca-475e-917e-2563e5a8cd19",
|
||||
"name": "test",
|
||||
"policies": [
|
||||
"anti-affinity"
|
||||
],
|
||||
"members": [],
|
||||
"metadata": {}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
// CreateOutput is a sample response to a Post call
|
||||
const CreateOutput = `
|
||||
{
|
||||
"server_group": {
|
||||
"id": "616fb98f-46ca-475e-917e-2563e5a8cd19",
|
||||
"name": "test",
|
||||
"policies": [
|
||||
"anti-affinity"
|
||||
],
|
||||
"members": [],
|
||||
"metadata": {}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
// FirstServerGroup is the first result in ListOutput.
|
||||
var FirstServerGroup = ServerGroup{
|
||||
ID: "616fb98f-46ca-475e-917e-2563e5a8cd19",
|
||||
Name: "test",
|
||||
Policies: []string{
|
||||
"anti-affinity",
|
||||
},
|
||||
Members: []string{},
|
||||
Metadata: map[string]interface{}{},
|
||||
}
|
||||
|
||||
// SecondServerGroup is the second result in ListOutput.
|
||||
var SecondServerGroup = ServerGroup{
|
||||
ID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
|
||||
Name: "test2",
|
||||
Policies: []string{
|
||||
"affinity",
|
||||
},
|
||||
Members: []string{},
|
||||
Metadata: map[string]interface{}{},
|
||||
}
|
||||
|
||||
// ExpectedServerGroupSlice is the slice of results that should be parsed
|
||||
// from ListOutput, in the expected order.
|
||||
var ExpectedServerGroupSlice = []ServerGroup{FirstServerGroup, SecondServerGroup}
|
||||
|
||||
// CreatedServerGroup is the parsed result from CreateOutput.
|
||||
var CreatedServerGroup = ServerGroup{
|
||||
ID: "616fb98f-46ca-475e-917e-2563e5a8cd19",
|
||||
Name: "test",
|
||||
Policies: []string{
|
||||
"anti-affinity",
|
||||
},
|
||||
Members: []string{},
|
||||
Metadata: map[string]interface{}{},
|
||||
}
|
||||
|
||||
// HandleListSuccessfully configures the test server to respond to a List request.
|
||||
func HandleListSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/os-server-groups", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
fmt.Fprintf(w, ListOutput)
|
||||
})
|
||||
}
|
||||
|
||||
// HandleGetSuccessfully configures the test server to respond to a Get request
|
||||
// for an existing server group
|
||||
func HandleGetSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/os-server-groups/4d8c3732-a248-40ed-bebc-539a6ffd25c0", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
fmt.Fprintf(w, GetOutput)
|
||||
})
|
||||
}
|
||||
|
||||
// HandleCreateSuccessfully configures the test server to respond to a Create request
|
||||
// for a new server group
|
||||
func HandleCreateSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/os-server-groups", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"server_group": {
|
||||
"name": "test",
|
||||
"policies": [
|
||||
"anti-affinity"
|
||||
]
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
fmt.Fprintf(w, CreateOutput)
|
||||
})
|
||||
}
|
||||
|
||||
// HandleDeleteSuccessfully configures the test server to respond to a Delete request for a
|
||||
// an existing server group
|
||||
func HandleDeleteSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/os-server-groups/616fb98f-46ca-475e-917e-2563e5a8cd19", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "DELETE")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
})
|
||||
}
|
77
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/servergroups/requests.go
generated
vendored
Normal file
77
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/servergroups/requests.go
generated
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
package servergroups
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// List returns a Pager that allows you to iterate over a collection of ServerGroups.
|
||||
func List(client *gophercloud.ServiceClient) pagination.Pager {
|
||||
return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page {
|
||||
return ServerGroupsPage{pagination.SinglePageBase(r)}
|
||||
})
|
||||
}
|
||||
|
||||
// CreateOptsBuilder describes struct types that can be accepted by the Create call. Notably, the
|
||||
// CreateOpts struct in this package does.
|
||||
type CreateOptsBuilder interface {
|
||||
ToServerGroupCreateMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// CreateOpts specifies a Server Group allocation request
|
||||
type CreateOpts struct {
|
||||
// Name is the name of the server group
|
||||
Name string
|
||||
|
||||
// Policies are the server group policies
|
||||
Policies []string
|
||||
}
|
||||
|
||||
// ToServerGroupCreateMap constructs a request body from CreateOpts.
|
||||
func (opts CreateOpts) ToServerGroupCreateMap() (map[string]interface{}, error) {
|
||||
if opts.Name == "" {
|
||||
return nil, errors.New("Missing field required for server group creation: Name")
|
||||
}
|
||||
|
||||
if len(opts.Policies) < 1 {
|
||||
return nil, errors.New("Missing field required for server group creation: Policies")
|
||||
}
|
||||
|
||||
serverGroup := make(map[string]interface{})
|
||||
serverGroup["name"] = opts.Name
|
||||
serverGroup["policies"] = opts.Policies
|
||||
|
||||
return map[string]interface{}{"server_group": serverGroup}, nil
|
||||
}
|
||||
|
||||
// Create requests the creation of a new Server Group
|
||||
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
|
||||
var res CreateResult
|
||||
|
||||
reqBody, err := opts.ToServerGroupCreateMap()
|
||||
if err != nil {
|
||||
res.Err = err
|
||||
return res
|
||||
}
|
||||
|
||||
_, res.Err = client.Post(createURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200},
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
// Get returns data about a previously created ServerGroup.
|
||||
func Get(client *gophercloud.ServiceClient, id string) GetResult {
|
||||
var res GetResult
|
||||
_, res.Err = client.Get(getURL(client, id), &res.Body, nil)
|
||||
return res
|
||||
}
|
||||
|
||||
// Delete requests the deletion of a previously allocated ServerGroup.
|
||||
func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
|
||||
var res DeleteResult
|
||||
_, res.Err = client.Delete(deleteURL(client, id), nil)
|
||||
return res
|
||||
}
|
87
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/servergroups/results.go
generated
vendored
Normal file
87
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/servergroups/results.go
generated
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
package servergroups
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// A ServerGroup creates a policy for instance placement in the cloud
|
||||
type ServerGroup struct {
|
||||
// ID is the unique ID of the Server Group.
|
||||
ID string `mapstructure:"id"`
|
||||
|
||||
// Name is the common name of the server group.
|
||||
Name string `mapstructure:"name"`
|
||||
|
||||
// Polices are the group policies.
|
||||
Policies []string `mapstructure:"policies"`
|
||||
|
||||
// Members are the members of the server group.
|
||||
Members []string `mapstructure:"members"`
|
||||
|
||||
// Metadata includes a list of all user-specified key-value pairs attached to the Server Group.
|
||||
Metadata map[string]interface{}
|
||||
}
|
||||
|
||||
// ServerGroupsPage stores a single, only page of ServerGroups
|
||||
// results from a List call.
|
||||
type ServerGroupsPage struct {
|
||||
pagination.SinglePageBase
|
||||
}
|
||||
|
||||
// IsEmpty determines whether or not a ServerGroupsPage is empty.
|
||||
func (page ServerGroupsPage) IsEmpty() (bool, error) {
|
||||
va, err := ExtractServerGroups(page)
|
||||
return len(va) == 0, err
|
||||
}
|
||||
|
||||
// ExtractServerGroups interprets a page of results as a slice of
|
||||
// ServerGroups.
|
||||
func ExtractServerGroups(page pagination.Page) ([]ServerGroup, error) {
|
||||
casted := page.(ServerGroupsPage).Body
|
||||
var response struct {
|
||||
ServerGroups []ServerGroup `mapstructure:"server_groups"`
|
||||
}
|
||||
|
||||
err := mapstructure.WeakDecode(casted, &response)
|
||||
|
||||
return response.ServerGroups, err
|
||||
}
|
||||
|
||||
type ServerGroupResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// Extract is a method that attempts to interpret any Server Group resource
|
||||
// response as a ServerGroup struct.
|
||||
func (r ServerGroupResult) Extract() (*ServerGroup, error) {
|
||||
if r.Err != nil {
|
||||
return nil, r.Err
|
||||
}
|
||||
|
||||
var res struct {
|
||||
ServerGroup *ServerGroup `json:"server_group" mapstructure:"server_group"`
|
||||
}
|
||||
|
||||
err := mapstructure.WeakDecode(r.Body, &res)
|
||||
return res.ServerGroup, err
|
||||
}
|
||||
|
||||
// CreateResult is the response from a Create operation. Call its Extract method to interpret it
|
||||
// as a ServerGroup.
|
||||
type CreateResult struct {
|
||||
ServerGroupResult
|
||||
}
|
||||
|
||||
// GetResult is the response from a Get operation. Call its Extract method to interpret it
|
||||
// as a ServerGroup.
|
||||
type GetResult struct {
|
||||
ServerGroupResult
|
||||
}
|
||||
|
||||
// DeleteResult is the response from a Delete operation. Call its Extract method to determine if
|
||||
// the call succeeded or failed.
|
||||
type DeleteResult struct {
|
||||
gophercloud.ErrResult
|
||||
}
|
25
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/servergroups/urls.go
generated
vendored
Normal file
25
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/servergroups/urls.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
package servergroups
|
||||
|
||||
import "github.com/rackspace/gophercloud"
|
||||
|
||||
const resourcePath = "os-server-groups"
|
||||
|
||||
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, id string) string {
|
||||
return c.ServiceURL(resourcePath, id)
|
||||
}
|
||||
|
||||
func deleteURL(c *gophercloud.ServiceClient, id string) string {
|
||||
return getURL(c, id)
|
||||
}
|
5
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/startstop/doc.go
generated
vendored
Normal file
5
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/startstop/doc.go
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
/*
|
||||
Package startstop provides functionality to start and stop servers that have
|
||||
been provisioned by the OpenStack Compute service.
|
||||
*/
|
||||
package startstop
|
27
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/startstop/fixtures.go
generated
vendored
Normal file
27
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/startstop/fixtures.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
package startstop
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
"github.com/rackspace/gophercloud/testhelper/client"
|
||||
)
|
||||
|
||||
func mockStartServerResponse(t *testing.T, id string) {
|
||||
th.Mux.HandleFunc("/servers/"+id+"/action", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
th.TestJSONRequest(t, r, `{"os-start": null}`)
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
})
|
||||
}
|
||||
|
||||
func mockStopServerResponse(t *testing.T, id string) {
|
||||
th.Mux.HandleFunc("/servers/"+id+"/action", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
th.TestJSONRequest(t, r, `{"os-stop": null}`)
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
})
|
||||
}
|
23
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/startstop/requests.go
generated
vendored
Normal file
23
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/startstop/requests.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
package startstop
|
||||
|
||||
import "github.com/rackspace/gophercloud"
|
||||
|
||||
func actionURL(client *gophercloud.ServiceClient, id string) string {
|
||||
return client.ServiceURL("servers", id, "action")
|
||||
}
|
||||
|
||||
// Start is the operation responsible for starting a Compute server.
|
||||
func Start(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult {
|
||||
var res gophercloud.ErrResult
|
||||
reqBody := map[string]interface{}{"os-start": nil}
|
||||
_, res.Err = client.Post(actionURL(client, id), reqBody, nil, nil)
|
||||
return res
|
||||
}
|
||||
|
||||
// Stop is the operation responsible for stopping a Compute server.
|
||||
func Stop(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult {
|
||||
var res gophercloud.ErrResult
|
||||
reqBody := map[string]interface{}{"os-stop": nil}
|
||||
_, res.Err = client.Post(actionURL(client, id), reqBody, nil, nil)
|
||||
return res
|
||||
}
|
2
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks/doc.go
generated
vendored
Normal file
2
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks/doc.go
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
// Package tenantnetworks provides the ability for tenants to see information about the networks they have access to
|
||||
package tenantnetworks
|
84
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks/fixtures.go
generated
vendored
Normal file
84
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks/fixtures.go
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
// +build fixtures
|
||||
|
||||
package tenantnetworks
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
"github.com/rackspace/gophercloud/testhelper/client"
|
||||
)
|
||||
|
||||
// ListOutput is a sample response to a List call.
|
||||
const ListOutput = `
|
||||
{
|
||||
"networks": [
|
||||
{
|
||||
"cidr": "10.0.0.0/29",
|
||||
"id": "20c8acc0-f747-4d71-a389-46d078ebf047",
|
||||
"label": "mynet_0"
|
||||
},
|
||||
{
|
||||
"cidr": "10.0.0.10/29",
|
||||
"id": "20c8acc0-f747-4d71-a389-46d078ebf000",
|
||||
"label": "mynet_1"
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
|
||||
// GetOutput is a sample response to a Get call.
|
||||
const GetOutput = `
|
||||
{
|
||||
"network": {
|
||||
"cidr": "10.0.0.10/29",
|
||||
"id": "20c8acc0-f747-4d71-a389-46d078ebf000",
|
||||
"label": "mynet_1"
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
// FirstNetwork is the first result in ListOutput.
|
||||
var nilTime time.Time
|
||||
var FirstNetwork = Network{
|
||||
CIDR: "10.0.0.0/29",
|
||||
ID: "20c8acc0-f747-4d71-a389-46d078ebf047",
|
||||
Name: "mynet_0",
|
||||
}
|
||||
|
||||
// SecondNetwork is the second result in ListOutput.
|
||||
var SecondNetwork = Network{
|
||||
CIDR: "10.0.0.10/29",
|
||||
ID: "20c8acc0-f747-4d71-a389-46d078ebf000",
|
||||
Name: "mynet_1",
|
||||
}
|
||||
|
||||
// ExpectedNetworkSlice is the slice of results that should be parsed
|
||||
// from ListOutput, in the expected order.
|
||||
var ExpectedNetworkSlice = []Network{FirstNetwork, SecondNetwork}
|
||||
|
||||
// HandleListSuccessfully configures the test server to respond to a List request.
|
||||
func HandleListSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/os-tenant-networks", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
fmt.Fprintf(w, ListOutput)
|
||||
})
|
||||
}
|
||||
|
||||
// HandleGetSuccessfully configures the test server to respond to a Get request
|
||||
// for an existing network.
|
||||
func HandleGetSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/os-tenant-networks/20c8acc0-f747-4d71-a389-46d078ebf000", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
fmt.Fprintf(w, GetOutput)
|
||||
})
|
||||
}
|
22
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks/requests.go
generated
vendored
Normal file
22
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks/requests.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
package tenantnetworks
|
||||
|
||||
import (
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// List returns a Pager that allows you to iterate over a collection of Network.
|
||||
func List(client *gophercloud.ServiceClient) pagination.Pager {
|
||||
url := listURL(client)
|
||||
createPage := func(r pagination.PageResult) pagination.Page {
|
||||
return NetworkPage{pagination.SinglePageBase(r)}
|
||||
}
|
||||
return pagination.NewPager(client, url, createPage)
|
||||
}
|
||||
|
||||
// Get returns data about a previously created Network.
|
||||
func Get(client *gophercloud.ServiceClient, id string) GetResult {
|
||||
var res GetResult
|
||||
_, res.Err = client.Get(getURL(client, id), &res.Body, nil)
|
||||
return res
|
||||
}
|
68
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks/results.go
generated
vendored
Normal file
68
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks/results.go
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
package tenantnetworks
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// A Network represents a nova-network that an instance communicates on
|
||||
type Network struct {
|
||||
// CIDR is the IPv4 subnet.
|
||||
CIDR string `mapstructure:"cidr"`
|
||||
|
||||
// ID is the UUID of the network.
|
||||
ID string `mapstructure:"id"`
|
||||
|
||||
// Name is the common name that the network has.
|
||||
Name string `mapstructure:"label"`
|
||||
}
|
||||
|
||||
// NetworkPage stores a single, only page of Networks
|
||||
// results from a List call.
|
||||
type NetworkPage struct {
|
||||
pagination.SinglePageBase
|
||||
}
|
||||
|
||||
// IsEmpty determines whether or not a NetworkPage is empty.
|
||||
func (page NetworkPage) IsEmpty() (bool, error) {
|
||||
va, err := ExtractNetworks(page)
|
||||
return len(va) == 0, err
|
||||
}
|
||||
|
||||
// ExtractNetworks interprets a page of results as a slice of Networks
|
||||
func ExtractNetworks(page pagination.Page) ([]Network, error) {
|
||||
networks := page.(NetworkPage).Body
|
||||
var res struct {
|
||||
Networks []Network `mapstructure:"networks"`
|
||||
}
|
||||
|
||||
err := mapstructure.WeakDecode(networks, &res)
|
||||
|
||||
return res.Networks, err
|
||||
}
|
||||
|
||||
type NetworkResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// Extract is a method that attempts to interpret any Network resource
|
||||
// response as a Network struct.
|
||||
func (r NetworkResult) Extract() (*Network, error) {
|
||||
if r.Err != nil {
|
||||
return nil, r.Err
|
||||
}
|
||||
|
||||
var res struct {
|
||||
Network *Network `json:"network" mapstructure:"network"`
|
||||
}
|
||||
|
||||
err := mapstructure.Decode(r.Body, &res)
|
||||
return res.Network, err
|
||||
}
|
||||
|
||||
// GetResult is the response from a Get operation. Call its Extract method to interpret it
|
||||
// as a Network.
|
||||
type GetResult struct {
|
||||
NetworkResult
|
||||
}
|
17
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks/urls.go
generated
vendored
Normal file
17
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks/urls.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
package tenantnetworks
|
||||
|
||||
import "github.com/rackspace/gophercloud"
|
||||
|
||||
const resourcePath = "os-tenant-networks"
|
||||
|
||||
func resourceURL(c *gophercloud.ServiceClient) string {
|
||||
return c.ServiceURL(resourcePath)
|
||||
}
|
||||
|
||||
func listURL(c *gophercloud.ServiceClient) string {
|
||||
return resourceURL(c)
|
||||
}
|
||||
|
||||
func getURL(c *gophercloud.ServiceClient, id string) string {
|
||||
return c.ServiceURL(resourcePath, id)
|
||||
}
|
3
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach/doc.go
generated
vendored
Normal file
3
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach/doc.go
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
// Package volumeattach provides the ability to attach and detach volumes
|
||||
// to instances
|
||||
package volumeattach
|
75
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach/requests.go
generated
vendored
Normal file
75
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach/requests.go
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
package volumeattach
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// List returns a Pager that allows you to iterate over a collection of VolumeAttachments.
|
||||
func List(client *gophercloud.ServiceClient, serverId string) pagination.Pager {
|
||||
return pagination.NewPager(client, listURL(client, serverId), func(r pagination.PageResult) pagination.Page {
|
||||
return VolumeAttachmentsPage{pagination.SinglePageBase(r)}
|
||||
})
|
||||
}
|
||||
|
||||
// CreateOptsBuilder describes struct types that can be accepted by the Create call. Notable, the
|
||||
// CreateOpts struct in this package does.
|
||||
type CreateOptsBuilder interface {
|
||||
ToVolumeAttachmentCreateMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// CreateOpts specifies volume attachment creation or import parameters.
|
||||
type CreateOpts struct {
|
||||
// Device is the device that the volume will attach to the instance as. Omit for "auto"
|
||||
Device string
|
||||
|
||||
// VolumeID is the ID of the volume to attach to the instance
|
||||
VolumeID string
|
||||
}
|
||||
|
||||
// ToVolumeAttachmentCreateMap constructs a request body from CreateOpts.
|
||||
func (opts CreateOpts) ToVolumeAttachmentCreateMap() (map[string]interface{}, error) {
|
||||
if opts.VolumeID == "" {
|
||||
return nil, errors.New("Missing field required for volume attachment creation: VolumeID")
|
||||
}
|
||||
|
||||
volumeAttachment := make(map[string]interface{})
|
||||
volumeAttachment["volumeId"] = opts.VolumeID
|
||||
if opts.Device != "" {
|
||||
volumeAttachment["device"] = opts.Device
|
||||
}
|
||||
|
||||
return map[string]interface{}{"volumeAttachment": volumeAttachment}, nil
|
||||
}
|
||||
|
||||
// Create requests the creation of a new volume attachment on the server
|
||||
func Create(client *gophercloud.ServiceClient, serverId string, opts CreateOptsBuilder) CreateResult {
|
||||
var res CreateResult
|
||||
|
||||
reqBody, err := opts.ToVolumeAttachmentCreateMap()
|
||||
if err != nil {
|
||||
res.Err = err
|
||||
return res
|
||||
}
|
||||
|
||||
_, res.Err = client.Post(createURL(client, serverId), reqBody, &res.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200},
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
// Get returns public data about a previously created VolumeAttachment.
|
||||
func Get(client *gophercloud.ServiceClient, serverId, aId string) GetResult {
|
||||
var res GetResult
|
||||
_, res.Err = client.Get(getURL(client, serverId, aId), &res.Body, nil)
|
||||
return res
|
||||
}
|
||||
|
||||
// Delete requests the deletion of a previous stored VolumeAttachment from the server.
|
||||
func Delete(client *gophercloud.ServiceClient, serverId, aId string) DeleteResult {
|
||||
var res DeleteResult
|
||||
_, res.Err = client.Delete(deleteURL(client, serverId, aId), nil)
|
||||
return res
|
||||
}
|
84
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach/results.go
generated
vendored
Normal file
84
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach/results.go
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
package volumeattach
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// VolumeAttach controls the attachment of a volume to an instance.
|
||||
type VolumeAttachment struct {
|
||||
// ID is a unique id of the attachment
|
||||
ID string `mapstructure:"id"`
|
||||
|
||||
// Device is what device the volume is attached as
|
||||
Device string `mapstructure:"device"`
|
||||
|
||||
// VolumeID is the ID of the attached volume
|
||||
VolumeID string `mapstructure:"volumeId"`
|
||||
|
||||
// ServerID is the ID of the instance that has the volume attached
|
||||
ServerID string `mapstructure:"serverId"`
|
||||
}
|
||||
|
||||
// VolumeAttachmentsPage stores a single, only page of VolumeAttachments
|
||||
// results from a List call.
|
||||
type VolumeAttachmentsPage struct {
|
||||
pagination.SinglePageBase
|
||||
}
|
||||
|
||||
// IsEmpty determines whether or not a VolumeAttachmentsPage is empty.
|
||||
func (page VolumeAttachmentsPage) IsEmpty() (bool, error) {
|
||||
va, err := ExtractVolumeAttachments(page)
|
||||
return len(va) == 0, err
|
||||
}
|
||||
|
||||
// ExtractVolumeAttachments interprets a page of results as a slice of
|
||||
// VolumeAttachments.
|
||||
func ExtractVolumeAttachments(page pagination.Page) ([]VolumeAttachment, error) {
|
||||
casted := page.(VolumeAttachmentsPage).Body
|
||||
var response struct {
|
||||
VolumeAttachments []VolumeAttachment `mapstructure:"volumeAttachments"`
|
||||
}
|
||||
|
||||
err := mapstructure.WeakDecode(casted, &response)
|
||||
|
||||
return response.VolumeAttachments, err
|
||||
}
|
||||
|
||||
type VolumeAttachmentResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// Extract is a method that attempts to interpret any VolumeAttachment resource
|
||||
// response as a VolumeAttachment struct.
|
||||
func (r VolumeAttachmentResult) Extract() (*VolumeAttachment, error) {
|
||||
if r.Err != nil {
|
||||
return nil, r.Err
|
||||
}
|
||||
|
||||
var res struct {
|
||||
VolumeAttachment *VolumeAttachment `json:"volumeAttachment" mapstructure:"volumeAttachment"`
|
||||
}
|
||||
|
||||
err := mapstructure.Decode(r.Body, &res)
|
||||
return res.VolumeAttachment, err
|
||||
}
|
||||
|
||||
// CreateResult is the response from a Create operation. Call its Extract method to interpret it
|
||||
// as a VolumeAttachment.
|
||||
type CreateResult struct {
|
||||
VolumeAttachmentResult
|
||||
}
|
||||
|
||||
// GetResult is the response from a Get operation. Call its Extract method to interpret it
|
||||
// as a VolumeAttachment.
|
||||
type GetResult struct {
|
||||
VolumeAttachmentResult
|
||||
}
|
||||
|
||||
// DeleteResult is the response from a Delete operation. Call its Extract method to determine if
|
||||
// the call succeeded or failed.
|
||||
type DeleteResult struct {
|
||||
gophercloud.ErrResult
|
||||
}
|
7
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach/testing/doc.go
generated
vendored
Normal file
7
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach/testing/doc.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
This is package created is to hold fixtures (which imports testing),
|
||||
so that importing volumeattach package does not inadvertently import testing into production code
|
||||
More information here:
|
||||
https://github.com/rackspace/gophercloud/issues/473
|
||||
*/
|
||||
package testing
|
110
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach/testing/fixtures.go
generated
vendored
Normal file
110
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach/testing/fixtures.go
generated
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
// +build fixtures
|
||||
|
||||
package testing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
"github.com/rackspace/gophercloud/testhelper/client"
|
||||
)
|
||||
|
||||
// ListOutput is a sample response to a List call.
|
||||
const ListOutput = `
|
||||
{
|
||||
"volumeAttachments": [
|
||||
{
|
||||
"device": "/dev/vdd",
|
||||
"id": "a26887c6-c47b-4654-abb5-dfadf7d3f803",
|
||||
"serverId": "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
|
||||
"volumeId": "a26887c6-c47b-4654-abb5-dfadf7d3f803"
|
||||
},
|
||||
{
|
||||
"device": "/dev/vdc",
|
||||
"id": "a26887c6-c47b-4654-abb5-dfadf7d3f804",
|
||||
"serverId": "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
|
||||
"volumeId": "a26887c6-c47b-4654-abb5-dfadf7d3f804"
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
|
||||
// GetOutput is a sample response to a Get call.
|
||||
const GetOutput = `
|
||||
{
|
||||
"volumeAttachment": {
|
||||
"device": "/dev/vdc",
|
||||
"id": "a26887c6-c47b-4654-abb5-dfadf7d3f804",
|
||||
"serverId": "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
|
||||
"volumeId": "a26887c6-c47b-4654-abb5-dfadf7d3f804"
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
// CreateOutput is a sample response to a Create call.
|
||||
const CreateOutput = `
|
||||
{
|
||||
"volumeAttachment": {
|
||||
"device": "/dev/vdc",
|
||||
"id": "a26887c6-c47b-4654-abb5-dfadf7d3f804",
|
||||
"serverId": "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
|
||||
"volumeId": "a26887c6-c47b-4654-abb5-dfadf7d3f804"
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
// HandleListSuccessfully configures the test server to respond to a List request.
|
||||
func HandleListSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/os-volume_attachments", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
fmt.Fprintf(w, ListOutput)
|
||||
})
|
||||
}
|
||||
|
||||
// HandleGetSuccessfully configures the test server to respond to a Get request
|
||||
// for an existing attachment
|
||||
func HandleGetSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/os-volume_attachments/a26887c6-c47b-4654-abb5-dfadf7d3f804", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
fmt.Fprintf(w, GetOutput)
|
||||
})
|
||||
}
|
||||
|
||||
// HandleCreateSuccessfully configures the test server to respond to a Create request
|
||||
// for a new attachment
|
||||
func HandleCreateSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/os-volume_attachments", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"volumeAttachment": {
|
||||
"volumeId": "a26887c6-c47b-4654-abb5-dfadf7d3f804",
|
||||
"device": "/dev/vdc"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
fmt.Fprintf(w, CreateOutput)
|
||||
})
|
||||
}
|
||||
|
||||
// HandleDeleteSuccessfully configures the test server to respond to a Delete request for a
|
||||
// an existing attachment
|
||||
func HandleDeleteSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/os-volume_attachments/a26887c6-c47b-4654-abb5-dfadf7d3f804", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "DELETE")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
})
|
||||
}
|
25
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach/urls.go
generated
vendored
Normal file
25
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach/urls.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
package volumeattach
|
||||
|
||||
import "github.com/rackspace/gophercloud"
|
||||
|
||||
const resourcePath = "os-volume_attachments"
|
||||
|
||||
func resourceURL(c *gophercloud.ServiceClient, serverId string) string {
|
||||
return c.ServiceURL("servers", serverId, resourcePath)
|
||||
}
|
||||
|
||||
func listURL(c *gophercloud.ServiceClient, serverId string) string {
|
||||
return resourceURL(c, serverId)
|
||||
}
|
||||
|
||||
func createURL(c *gophercloud.ServiceClient, serverId string) string {
|
||||
return resourceURL(c, serverId)
|
||||
}
|
||||
|
||||
func getURL(c *gophercloud.ServiceClient, serverId, aId string) string {
|
||||
return c.ServiceURL("servers", serverId, resourcePath, aId)
|
||||
}
|
||||
|
||||
func deleteURL(c *gophercloud.ServiceClient, serverId, aId string) string {
|
||||
return getURL(c, serverId, aId)
|
||||
}
|
7
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/flavors/doc.go
generated
vendored
Normal file
7
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/flavors/doc.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
// Package flavors provides information and interaction with the flavor API
|
||||
// resource 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.
|
||||
package flavors
|
103
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/flavors/requests.go
generated
vendored
Normal file
103
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/flavors/requests.go
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
package flavors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// ListOptsBuilder allows extensions to add additional parameters to the
|
||||
// List request.
|
||||
type ListOptsBuilder interface {
|
||||
ToFlavorListQuery() (string, error)
|
||||
}
|
||||
|
||||
// ListOpts helps control 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"`
|
||||
}
|
||||
|
||||
// ToFlavorListQuery formats a ListOpts into a query string.
|
||||
func (opts ListOpts) ToFlavorListQuery() (string, error) {
|
||||
q, err := gophercloud.BuildQueryString(opts)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return q.String(), nil
|
||||
}
|
||||
|
||||
// ListDetail instructs OpenStack to provide a list of flavors.
|
||||
// You may provide criteria by which List curtails its results for easier processing.
|
||||
// See ListOpts for more details.
|
||||
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
|
||||
}
|
||||
createPage := func(r pagination.PageResult) pagination.Page {
|
||||
return FlavorPage{pagination.LinkedPageBase{PageResult: r}}
|
||||
}
|
||||
|
||||
return pagination.NewPager(client, url, createPage)
|
||||
}
|
||||
|
||||
// Get instructs OpenStack to provide details on a single flavor, identified by its ID.
|
||||
// Use ExtractFlavor to convert its result into a Flavor.
|
||||
func Get(client *gophercloud.ServiceClient, id string) GetResult {
|
||||
var res GetResult
|
||||
_, res.Err = client.Get(getURL(client, id), &res.Body, nil)
|
||||
return res
|
||||
}
|
||||
|
||||
// IDFromName is a convienience function that returns a flavor's ID given its name.
|
||||
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
|
||||
flavorCount := 0
|
||||
flavorID := ""
|
||||
if name == "" {
|
||||
return "", fmt.Errorf("A flavor name must be provided.")
|
||||
}
|
||||
pager := ListDetail(client, nil)
|
||||
pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
flavorList, err := ExtractFlavors(page)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, f := range flavorList {
|
||||
if f.Name == name {
|
||||
flavorCount++
|
||||
flavorID = f.ID
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
|
||||
switch flavorCount {
|
||||
case 0:
|
||||
return "", fmt.Errorf("Unable to find flavor: %s", name)
|
||||
case 1:
|
||||
return flavorID, nil
|
||||
default:
|
||||
return "", fmt.Errorf("Found %d flavors matching %s", flavorCount, name)
|
||||
}
|
||||
}
|
122
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/flavors/results.go
generated
vendored
Normal file
122
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/flavors/results.go
generated
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
package flavors
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// ErrCannotInterpret is returned by an Extract call if the response body doesn't have the expected structure.
|
||||
var ErrCannotInterpet = errors.New("Unable to interpret a response body.")
|
||||
|
||||
// GetResult temporarily holds the response from a Get call.
|
||||
type GetResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// Extract provides access to the individual Flavor returned by the Get function.
|
||||
func (gr GetResult) Extract() (*Flavor, error) {
|
||||
if gr.Err != nil {
|
||||
return nil, gr.Err
|
||||
}
|
||||
|
||||
var result struct {
|
||||
Flavor Flavor `mapstructure:"flavor"`
|
||||
}
|
||||
|
||||
cfg := &mapstructure.DecoderConfig{
|
||||
DecodeHook: defaulter,
|
||||
Result: &result,
|
||||
}
|
||||
decoder, err := mapstructure.NewDecoder(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = decoder.Decode(gr.Body)
|
||||
return &result.Flavor, err
|
||||
}
|
||||
|
||||
// Flavor records represent (virtual) hardware configurations for server resources in a region.
|
||||
type Flavor struct {
|
||||
// The Id field contains the flavor's unique identifier.
|
||||
// For example, this identifier will be useful when specifying which hardware configuration to use for a new server instance.
|
||||
ID string `mapstructure:"id"`
|
||||
|
||||
// The Disk and RA< fields provide a measure of storage space offered by the flavor, in GB and MB, respectively.
|
||||
Disk int `mapstructure:"disk"`
|
||||
RAM int `mapstructure:"ram"`
|
||||
|
||||
// The Name field provides a human-readable moniker for the flavor.
|
||||
Name string `mapstructure:"name"`
|
||||
|
||||
RxTxFactor float64 `mapstructure:"rxtx_factor"`
|
||||
|
||||
// Swap indicates how much space is reserved for swap.
|
||||
// If not provided, this field will be set to 0.
|
||||
Swap int `mapstructure:"swap"`
|
||||
|
||||
// VCPUs indicates how many (virtual) CPUs are available for this flavor.
|
||||
VCPUs int `mapstructure:"vcpus"`
|
||||
}
|
||||
|
||||
// FlavorPage contains a single page of the response from a List call.
|
||||
type FlavorPage struct {
|
||||
pagination.LinkedPageBase
|
||||
}
|
||||
|
||||
// IsEmpty determines if a page contains any results.
|
||||
func (p FlavorPage) IsEmpty() (bool, error) {
|
||||
flavors, err := ExtractFlavors(p)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
return len(flavors) == 0, nil
|
||||
}
|
||||
|
||||
// NextPageURL uses the response's embedded link reference to navigate to the next page of results.
|
||||
func (p FlavorPage) NextPageURL() (string, error) {
|
||||
type resp struct {
|
||||
Links []gophercloud.Link `mapstructure:"flavors_links"`
|
||||
}
|
||||
|
||||
var r resp
|
||||
err := mapstructure.Decode(p.Body, &r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return gophercloud.ExtractNextURL(r.Links)
|
||||
}
|
||||
|
||||
func defaulter(from, to reflect.Kind, v interface{}) (interface{}, error) {
|
||||
if (from == reflect.String) && (to == reflect.Int) {
|
||||
return 0, nil
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// ExtractFlavors provides access to the list of flavors in a page acquired from the List operation.
|
||||
func ExtractFlavors(page pagination.Page) ([]Flavor, error) {
|
||||
casted := page.(FlavorPage).Body
|
||||
var container struct {
|
||||
Flavors []Flavor `mapstructure:"flavors"`
|
||||
}
|
||||
|
||||
cfg := &mapstructure.DecoderConfig{
|
||||
DecodeHook: defaulter,
|
||||
Result: &container,
|
||||
}
|
||||
decoder, err := mapstructure.NewDecoder(cfg)
|
||||
if err != nil {
|
||||
return container.Flavors, err
|
||||
}
|
||||
err = decoder.Decode(casted)
|
||||
if err != nil {
|
||||
return container.Flavors, err
|
||||
}
|
||||
|
||||
return container.Flavors, nil
|
||||
}
|
13
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/flavors/urls.go
generated
vendored
Normal file
13
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/flavors/urls.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
package flavors
|
||||
|
||||
import (
|
||||
"github.com/rackspace/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")
|
||||
}
|
7
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/images/doc.go
generated
vendored
Normal file
7
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/images/doc.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
// Package images provides information and interaction with the image API
|
||||
// resource in the OpenStack Compute 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.
|
||||
package images
|
109
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/images/requests.go
generated
vendored
Normal file
109
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/images/requests.go
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
package images
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// ListOptsBuilder allows extensions to add additional parameters to the
|
||||
// List request.
|
||||
type ListOptsBuilder interface {
|
||||
ToImageListQuery() (string, error)
|
||||
}
|
||||
|
||||
// ListOpts contain options for limiting the number of Images returned from a call to ListDetail.
|
||||
type ListOpts struct {
|
||||
// When the image last changed status (in date-time format).
|
||||
ChangesSince string `q:"changes-since"`
|
||||
// The number of Images to return.
|
||||
Limit int `q:"limit"`
|
||||
// UUID of the Image at which to set a marker.
|
||||
Marker string `q:"marker"`
|
||||
// The name of the Image.
|
||||
Name string `q:"name"`
|
||||
// The name of the Server (in URL format).
|
||||
Server string `q:"server"`
|
||||
// The current status of the Image.
|
||||
Status string `q:"status"`
|
||||
// The value of 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)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return q.String(), nil
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
createPage := func(r pagination.PageResult) pagination.Page {
|
||||
return ImagePage{pagination.LinkedPageBase{PageResult: r}}
|
||||
}
|
||||
|
||||
return pagination.NewPager(client, url, createPage)
|
||||
}
|
||||
|
||||
// Get acquires additional detail about a specific image by ID.
|
||||
// Use ExtractImage() to interpret the result as an openstack Image.
|
||||
func Get(client *gophercloud.ServiceClient, id string) GetResult {
|
||||
var result GetResult
|
||||
_, result.Err = client.Get(getURL(client, id), &result.Body, nil)
|
||||
return result
|
||||
}
|
||||
|
||||
// Delete deletes the specified image ID.
|
||||
func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
|
||||
var result DeleteResult
|
||||
_, result.Err = client.Delete(deleteURL(client, id), nil)
|
||||
return result
|
||||
}
|
||||
|
||||
// IDFromName is a convienience function that returns an image's ID given its name.
|
||||
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
|
||||
imageCount := 0
|
||||
imageID := ""
|
||||
if name == "" {
|
||||
return "", fmt.Errorf("An image name must be provided.")
|
||||
}
|
||||
pager := ListDetail(client, &ListOpts{
|
||||
Name: name,
|
||||
})
|
||||
pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
imageList, err := ExtractImages(page)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, i := range imageList {
|
||||
if i.Name == name {
|
||||
imageCount++
|
||||
imageID = i.ID
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
|
||||
switch imageCount {
|
||||
case 0:
|
||||
return "", fmt.Errorf("Unable to find image: %s", name)
|
||||
case 1:
|
||||
return imageID, nil
|
||||
default:
|
||||
return "", fmt.Errorf("Found %d images matching %s", imageCount, name)
|
||||
}
|
||||
}
|
95
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/images/results.go
generated
vendored
Normal file
95
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/images/results.go
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
package images
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// GetResult temporarily stores a Get response.
|
||||
type GetResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// DeleteResult represents the result of an image.Delete operation.
|
||||
type DeleteResult struct {
|
||||
gophercloud.ErrResult
|
||||
}
|
||||
|
||||
// Extract interprets a GetResult as an Image.
|
||||
func (gr GetResult) Extract() (*Image, error) {
|
||||
if gr.Err != nil {
|
||||
return nil, gr.Err
|
||||
}
|
||||
|
||||
var decoded struct {
|
||||
Image Image `mapstructure:"image"`
|
||||
}
|
||||
|
||||
err := mapstructure.Decode(gr.Body, &decoded)
|
||||
return &decoded.Image, err
|
||||
}
|
||||
|
||||
// Image is used for JSON (un)marshalling.
|
||||
// It provides a description of an OS image.
|
||||
type Image struct {
|
||||
// ID contains the image's unique identifier.
|
||||
ID string
|
||||
|
||||
Created string
|
||||
|
||||
// MinDisk and MinRAM specify the minimum resources a server must provide to be able to install the image.
|
||||
MinDisk int
|
||||
MinRAM int
|
||||
|
||||
// Name provides a human-readable moniker for the OS image.
|
||||
Name string
|
||||
|
||||
// The Progress and Status fields indicate image-creation status.
|
||||
// Any usable image will have 100% progress.
|
||||
Progress int
|
||||
Status string
|
||||
|
||||
Updated string
|
||||
}
|
||||
|
||||
// ImagePage contains a single page of results from a List operation.
|
||||
// Use ExtractImages to convert it into a slice of usable structs.
|
||||
type ImagePage struct {
|
||||
pagination.LinkedPageBase
|
||||
}
|
||||
|
||||
// IsEmpty returns true if a page contains no Image results.
|
||||
func (page ImagePage) IsEmpty() (bool, error) {
|
||||
images, err := ExtractImages(page)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
return len(images) == 0, nil
|
||||
}
|
||||
|
||||
// NextPageURL uses the response's embedded link reference to navigate to the next page of results.
|
||||
func (page ImagePage) NextPageURL() (string, error) {
|
||||
type resp struct {
|
||||
Links []gophercloud.Link `mapstructure:"images_links"`
|
||||
}
|
||||
|
||||
var r resp
|
||||
err := mapstructure.Decode(page.Body, &r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return gophercloud.ExtractNextURL(r.Links)
|
||||
}
|
||||
|
||||
// ExtractImages converts a page of List results into a slice of usable Image structs.
|
||||
func ExtractImages(page pagination.Page) ([]Image, error) {
|
||||
casted := page.(ImagePage).Body
|
||||
var results struct {
|
||||
Images []Image `mapstructure:"images"`
|
||||
}
|
||||
|
||||
err := mapstructure.Decode(casted, &results)
|
||||
return results.Images, err
|
||||
}
|
15
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/images/urls.go
generated
vendored
Normal file
15
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/images/urls.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
package images
|
||||
|
||||
import "github.com/rackspace/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)
|
||||
}
|
6
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/doc.go
generated
vendored
Normal file
6
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/doc.go
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
// 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.
|
||||
package servers
|
692
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/fixtures.go
generated
vendored
Normal file
692
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/fixtures.go
generated
vendored
Normal file
@@ -0,0 +1,692 @@
|
||||
// +build fixtures
|
||||
|
||||
package servers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
"github.com/rackspace/gophercloud/testhelper/client"
|
||||
)
|
||||
|
||||
// ServerListBody contains the canned body of a servers.List response.
|
||||
const ServerListBody = `
|
||||
{
|
||||
"servers": [
|
||||
{
|
||||
"status": "ACTIVE",
|
||||
"updated": "2014-09-25T13:10:10Z",
|
||||
"hostId": "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362",
|
||||
"OS-EXT-SRV-ATTR:host": "devstack",
|
||||
"addresses": {
|
||||
"private": [
|
||||
{
|
||||
"OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:7c:1b:2b",
|
||||
"version": 4,
|
||||
"addr": "10.0.0.32",
|
||||
"OS-EXT-IPS:type": "fixed"
|
||||
}
|
||||
]
|
||||
},
|
||||
"links": [
|
||||
{
|
||||
"href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/ef079b0c-e610-4dfb-b1aa-b49f07ac48e5",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/ef079b0c-e610-4dfb-b1aa-b49f07ac48e5",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
],
|
||||
"key_name": null,
|
||||
"image": {
|
||||
"id": "f90f6034-2570-4974-8351-6b49732ef2eb",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"OS-EXT-STS:task_state": null,
|
||||
"OS-EXT-STS:vm_state": "active",
|
||||
"OS-EXT-SRV-ATTR:instance_name": "instance-0000001e",
|
||||
"OS-SRV-USG:launched_at": "2014-09-25T13:10:10.000000",
|
||||
"OS-EXT-SRV-ATTR:hypervisor_hostname": "devstack",
|
||||
"flavor": {
|
||||
"id": "1",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": "ef079b0c-e610-4dfb-b1aa-b49f07ac48e5",
|
||||
"security_groups": [
|
||||
{
|
||||
"name": "default"
|
||||
}
|
||||
],
|
||||
"OS-SRV-USG:terminated_at": null,
|
||||
"OS-EXT-AZ:availability_zone": "nova",
|
||||
"user_id": "9349aff8be7545ac9d2f1d00999a23cd",
|
||||
"name": "herp",
|
||||
"created": "2014-09-25T13:10:02Z",
|
||||
"tenant_id": "fcad67a6189847c4aecfa3c81a05783b",
|
||||
"OS-DCF:diskConfig": "MANUAL",
|
||||
"os-extended-volumes:volumes_attached": [],
|
||||
"accessIPv4": "",
|
||||
"accessIPv6": "",
|
||||
"progress": 0,
|
||||
"OS-EXT-STS:power_state": 1,
|
||||
"config_drive": "",
|
||||
"metadata": {}
|
||||
},
|
||||
{
|
||||
"status": "ACTIVE",
|
||||
"updated": "2014-09-25T13:04:49Z",
|
||||
"hostId": "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362",
|
||||
"OS-EXT-SRV-ATTR:host": "devstack",
|
||||
"addresses": {
|
||||
"private": [
|
||||
{
|
||||
"OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:9e:89:be",
|
||||
"version": 4,
|
||||
"addr": "10.0.0.31",
|
||||
"OS-EXT-IPS:type": "fixed"
|
||||
}
|
||||
]
|
||||
},
|
||||
"links": [
|
||||
{
|
||||
"href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
],
|
||||
"key_name": null,
|
||||
"image": {
|
||||
"id": "f90f6034-2570-4974-8351-6b49732ef2eb",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"OS-EXT-STS:task_state": null,
|
||||
"OS-EXT-STS:vm_state": "active",
|
||||
"OS-EXT-SRV-ATTR:instance_name": "instance-0000001d",
|
||||
"OS-SRV-USG:launched_at": "2014-09-25T13:04:49.000000",
|
||||
"OS-EXT-SRV-ATTR:hypervisor_hostname": "devstack",
|
||||
"flavor": {
|
||||
"id": "1",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": "9e5476bd-a4ec-4653-93d6-72c93aa682ba",
|
||||
"security_groups": [
|
||||
{
|
||||
"name": "default"
|
||||
}
|
||||
],
|
||||
"OS-SRV-USG:terminated_at": null,
|
||||
"OS-EXT-AZ:availability_zone": "nova",
|
||||
"user_id": "9349aff8be7545ac9d2f1d00999a23cd",
|
||||
"name": "derp",
|
||||
"created": "2014-09-25T13:04:41Z",
|
||||
"tenant_id": "fcad67a6189847c4aecfa3c81a05783b",
|
||||
"OS-DCF:diskConfig": "MANUAL",
|
||||
"os-extended-volumes:volumes_attached": [],
|
||||
"accessIPv4": "",
|
||||
"accessIPv6": "",
|
||||
"progress": 0,
|
||||
"OS-EXT-STS:power_state": 1,
|
||||
"config_drive": "",
|
||||
"metadata": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
|
||||
// SingleServerBody is the canned body of a Get request on an existing server.
|
||||
const SingleServerBody = `
|
||||
{
|
||||
"server": {
|
||||
"status": "ACTIVE",
|
||||
"updated": "2014-09-25T13:04:49Z",
|
||||
"hostId": "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362",
|
||||
"OS-EXT-SRV-ATTR:host": "devstack",
|
||||
"addresses": {
|
||||
"private": [
|
||||
{
|
||||
"OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:9e:89:be",
|
||||
"version": 4,
|
||||
"addr": "10.0.0.31",
|
||||
"OS-EXT-IPS:type": "fixed"
|
||||
}
|
||||
]
|
||||
},
|
||||
"links": [
|
||||
{
|
||||
"href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
],
|
||||
"key_name": null,
|
||||
"image": {
|
||||
"id": "f90f6034-2570-4974-8351-6b49732ef2eb",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"OS-EXT-STS:task_state": null,
|
||||
"OS-EXT-STS:vm_state": "active",
|
||||
"OS-EXT-SRV-ATTR:instance_name": "instance-0000001d",
|
||||
"OS-SRV-USG:launched_at": "2014-09-25T13:04:49.000000",
|
||||
"OS-EXT-SRV-ATTR:hypervisor_hostname": "devstack",
|
||||
"flavor": {
|
||||
"id": "1",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": "9e5476bd-a4ec-4653-93d6-72c93aa682ba",
|
||||
"security_groups": [
|
||||
{
|
||||
"name": "default"
|
||||
}
|
||||
],
|
||||
"OS-SRV-USG:terminated_at": null,
|
||||
"OS-EXT-AZ:availability_zone": "nova",
|
||||
"user_id": "9349aff8be7545ac9d2f1d00999a23cd",
|
||||
"name": "derp",
|
||||
"created": "2014-09-25T13:04:41Z",
|
||||
"tenant_id": "fcad67a6189847c4aecfa3c81a05783b",
|
||||
"OS-DCF:diskConfig": "MANUAL",
|
||||
"os-extended-volumes:volumes_attached": [],
|
||||
"accessIPv4": "",
|
||||
"accessIPv6": "",
|
||||
"progress": 0,
|
||||
"OS-EXT-STS:power_state": 1,
|
||||
"config_drive": "",
|
||||
"metadata": {}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const ServerPasswordBody = `
|
||||
{
|
||||
"password": "xlozO3wLCBRWAa2yDjCCVx8vwNPypxnypmRYDa/zErlQ+EzPe1S/Gz6nfmC52mOlOSCRuUOmG7kqqgejPof6M7bOezS387zjq4LSvvwp28zUknzy4YzfFGhnHAdai3TxUJ26pfQCYrq8UTzmKF2Bq8ioSEtVVzM0A96pDh8W2i7BOz6MdoiVyiev/I1K2LsuipfxSJR7Wdke4zNXJjHHP2RfYsVbZ/k9ANu+Nz4iIH8/7Cacud/pphH7EjrY6a4RZNrjQskrhKYed0YERpotyjYk1eDtRe72GrSiXteqCM4biaQ5w3ruS+AcX//PXk3uJ5kC7d67fPXaVz4WaQRYMg=="
|
||||
}
|
||||
`
|
||||
|
||||
var (
|
||||
// ServerHerp is a Server struct that should correspond to the first result in ServerListBody.
|
||||
ServerHerp = Server{
|
||||
Status: "ACTIVE",
|
||||
Updated: "2014-09-25T13:10:10Z",
|
||||
HostID: "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362",
|
||||
Addresses: map[string]interface{}{
|
||||
"private": []interface{}{
|
||||
map[string]interface{}{
|
||||
"OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:7c:1b:2b",
|
||||
"version": float64(4),
|
||||
"addr": "10.0.0.32",
|
||||
"OS-EXT-IPS:type": "fixed",
|
||||
},
|
||||
},
|
||||
},
|
||||
Links: []interface{}{
|
||||
map[string]interface{}{
|
||||
"href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/ef079b0c-e610-4dfb-b1aa-b49f07ac48e5",
|
||||
"rel": "self",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/ef079b0c-e610-4dfb-b1aa-b49f07ac48e5",
|
||||
"rel": "bookmark",
|
||||
},
|
||||
},
|
||||
Image: map[string]interface{}{
|
||||
"id": "f90f6034-2570-4974-8351-6b49732ef2eb",
|
||||
"links": []interface{}{
|
||||
map[string]interface{}{
|
||||
"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb",
|
||||
"rel": "bookmark",
|
||||
},
|
||||
},
|
||||
},
|
||||
Flavor: map[string]interface{}{
|
||||
"id": "1",
|
||||
"links": []interface{}{
|
||||
map[string]interface{}{
|
||||
"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1",
|
||||
"rel": "bookmark",
|
||||
},
|
||||
},
|
||||
},
|
||||
ID: "ef079b0c-e610-4dfb-b1aa-b49f07ac48e5",
|
||||
UserID: "9349aff8be7545ac9d2f1d00999a23cd",
|
||||
Name: "herp",
|
||||
Created: "2014-09-25T13:10:02Z",
|
||||
TenantID: "fcad67a6189847c4aecfa3c81a05783b",
|
||||
Metadata: map[string]interface{}{},
|
||||
SecurityGroups: []map[string]interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "default",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// ServerDerp is a Server struct that should correspond to the second server in ServerListBody.
|
||||
ServerDerp = Server{
|
||||
Status: "ACTIVE",
|
||||
Updated: "2014-09-25T13:04:49Z",
|
||||
HostID: "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362",
|
||||
Addresses: map[string]interface{}{
|
||||
"private": []interface{}{
|
||||
map[string]interface{}{
|
||||
"OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:9e:89:be",
|
||||
"version": float64(4),
|
||||
"addr": "10.0.0.31",
|
||||
"OS-EXT-IPS:type": "fixed",
|
||||
},
|
||||
},
|
||||
},
|
||||
Links: []interface{}{
|
||||
map[string]interface{}{
|
||||
"href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba",
|
||||
"rel": "self",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba",
|
||||
"rel": "bookmark",
|
||||
},
|
||||
},
|
||||
Image: map[string]interface{}{
|
||||
"id": "f90f6034-2570-4974-8351-6b49732ef2eb",
|
||||
"links": []interface{}{
|
||||
map[string]interface{}{
|
||||
"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb",
|
||||
"rel": "bookmark",
|
||||
},
|
||||
},
|
||||
},
|
||||
Flavor: map[string]interface{}{
|
||||
"id": "1",
|
||||
"links": []interface{}{
|
||||
map[string]interface{}{
|
||||
"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1",
|
||||
"rel": "bookmark",
|
||||
},
|
||||
},
|
||||
},
|
||||
ID: "9e5476bd-a4ec-4653-93d6-72c93aa682ba",
|
||||
UserID: "9349aff8be7545ac9d2f1d00999a23cd",
|
||||
Name: "derp",
|
||||
Created: "2014-09-25T13:04:41Z",
|
||||
TenantID: "fcad67a6189847c4aecfa3c81a05783b",
|
||||
Metadata: map[string]interface{}{},
|
||||
SecurityGroups: []map[string]interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "default",
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// HandleServerCreationSuccessfully sets up the test server to respond to a server creation request
|
||||
// with a given response.
|
||||
func HandleServerCreationSuccessfully(t *testing.T, response string) {
|
||||
th.Mux.HandleFunc("/servers", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
th.TestJSONRequest(t, r, `{
|
||||
"server": {
|
||||
"name": "derp",
|
||||
"imageRef": "f90f6034-2570-4974-8351-6b49732ef2eb",
|
||||
"flavorRef": "1"
|
||||
}
|
||||
}`)
|
||||
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
fmt.Fprintf(w, response)
|
||||
})
|
||||
}
|
||||
|
||||
// HandleServerListSuccessfully sets up the test server to respond to a server List request.
|
||||
func HandleServerListSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/servers/detail", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
r.ParseForm()
|
||||
marker := r.Form.Get("marker")
|
||||
switch marker {
|
||||
case "":
|
||||
fmt.Fprintf(w, ServerListBody)
|
||||
case "9e5476bd-a4ec-4653-93d6-72c93aa682ba":
|
||||
fmt.Fprintf(w, `{ "servers": [] }`)
|
||||
default:
|
||||
t.Fatalf("/servers/detail invoked with unexpected marker=[%s]", marker)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// HandleServerDeletionSuccessfully sets up the test server to respond to a server deletion request.
|
||||
func HandleServerDeletionSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/servers/asdfasdfasdf", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "DELETE")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
})
|
||||
}
|
||||
|
||||
// HandleServerForceDeletionSuccessfully sets up the test server to respond to a server force deletion
|
||||
// request.
|
||||
func HandleServerForceDeletionSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/servers/asdfasdfasdf/action", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
th.TestJSONRequest(t, r, `{ "forceDelete": "" }`)
|
||||
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
})
|
||||
}
|
||||
|
||||
// HandleServerGetSuccessfully sets up the test server to respond to a server Get request.
|
||||
func HandleServerGetSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/servers/1234asdf", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
|
||||
fmt.Fprintf(w, SingleServerBody)
|
||||
})
|
||||
}
|
||||
|
||||
// HandleServerUpdateSuccessfully sets up the test server to respond to a server Update request.
|
||||
func HandleServerUpdateSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/servers/1234asdf", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "PUT")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestJSONRequest(t, r, `{ "server": { "name": "new-name" } }`)
|
||||
|
||||
fmt.Fprintf(w, SingleServerBody)
|
||||
})
|
||||
}
|
||||
|
||||
// HandleAdminPasswordChangeSuccessfully sets up the test server to respond to a server password
|
||||
// change request.
|
||||
func HandleAdminPasswordChangeSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
th.TestJSONRequest(t, r, `{ "changePassword": { "adminPass": "new-password" } }`)
|
||||
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
})
|
||||
}
|
||||
|
||||
// HandleRebootSuccessfully sets up the test server to respond to a reboot request with success.
|
||||
func HandleRebootSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
th.TestJSONRequest(t, r, `{ "reboot": { "type": "SOFT" } }`)
|
||||
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
})
|
||||
}
|
||||
|
||||
// HandleRebuildSuccessfully sets up the test server to respond to a rebuild request with success.
|
||||
func HandleRebuildSuccessfully(t *testing.T, response string) {
|
||||
th.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"rebuild": {
|
||||
"name": "new-name",
|
||||
"adminPass": "swordfish",
|
||||
"imageRef": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb",
|
||||
"accessIPv4": "1.2.3.4"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
fmt.Fprintf(w, response)
|
||||
})
|
||||
}
|
||||
|
||||
// HandleServerRescueSuccessfully sets up the test server to respond to a server Rescue request.
|
||||
func HandleServerRescueSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
th.TestJSONRequest(t, r, `{ "rescue": { "adminPass": "1234567890" } }`)
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(`{ "adminPass": "1234567890" }`))
|
||||
})
|
||||
}
|
||||
|
||||
// HandleMetadatumGetSuccessfully sets up the test server to respond to a metadatum Get request.
|
||||
func HandleMetadatumGetSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/servers/1234asdf/metadata/foo", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.Write([]byte(`{ "meta": {"foo":"bar"}}`))
|
||||
})
|
||||
}
|
||||
|
||||
// HandleMetadatumCreateSuccessfully sets up the test server to respond to a metadatum Create request.
|
||||
func HandleMetadatumCreateSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/servers/1234asdf/metadata/foo", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "PUT")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
th.TestJSONRequest(t, r, `{
|
||||
"meta": {
|
||||
"foo": "bar"
|
||||
}
|
||||
}`)
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.Write([]byte(`{ "meta": {"foo":"bar"}}`))
|
||||
})
|
||||
}
|
||||
|
||||
// HandleMetadatumDeleteSuccessfully sets up the test server to respond to a metadatum Delete request.
|
||||
func HandleMetadatumDeleteSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/servers/1234asdf/metadata/foo", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "DELETE")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
})
|
||||
}
|
||||
|
||||
// HandleMetadataGetSuccessfully sets up the test server to respond to a metadata Get request.
|
||||
func HandleMetadataGetSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/servers/1234asdf/metadata", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(`{ "metadata": {"foo":"bar", "this":"that"}}`))
|
||||
})
|
||||
}
|
||||
|
||||
// HandleMetadataResetSuccessfully sets up the test server to respond to a metadata Create request.
|
||||
func HandleMetadataResetSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/servers/1234asdf/metadata", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "PUT")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
th.TestJSONRequest(t, r, `{
|
||||
"metadata": {
|
||||
"foo": "bar",
|
||||
"this": "that"
|
||||
}
|
||||
}`)
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.Write([]byte(`{ "metadata": {"foo":"bar", "this":"that"}}`))
|
||||
})
|
||||
}
|
||||
|
||||
// HandleMetadataUpdateSuccessfully sets up the test server to respond to a metadata Update request.
|
||||
func HandleMetadataUpdateSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/servers/1234asdf/metadata", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
th.TestJSONRequest(t, r, `{
|
||||
"metadata": {
|
||||
"foo": "baz",
|
||||
"this": "those"
|
||||
}
|
||||
}`)
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.Write([]byte(`{ "metadata": {"foo":"baz", "this":"those"}}`))
|
||||
})
|
||||
}
|
||||
|
||||
// ListAddressesExpected represents an expected repsonse from a ListAddresses request.
|
||||
var ListAddressesExpected = map[string][]Address{
|
||||
"public": []Address{
|
||||
Address{
|
||||
Version: 4,
|
||||
Address: "80.56.136.39",
|
||||
},
|
||||
Address{
|
||||
Version: 6,
|
||||
Address: "2001:4800:790e:510:be76:4eff:fe04:82a8",
|
||||
},
|
||||
},
|
||||
"private": []Address{
|
||||
Address{
|
||||
Version: 4,
|
||||
Address: "10.880.3.154",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// HandleAddressListSuccessfully sets up the test server to respond to a ListAddresses request.
|
||||
func HandleAddressListSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/servers/asdfasdfasdf/ips", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
fmt.Fprintf(w, `{
|
||||
"addresses": {
|
||||
"public": [
|
||||
{
|
||||
"version": 4,
|
||||
"addr": "50.56.176.35"
|
||||
},
|
||||
{
|
||||
"version": 6,
|
||||
"addr": "2001:4800:780e:510:be76:4eff:fe04:84a8"
|
||||
}
|
||||
],
|
||||
"private": [
|
||||
{
|
||||
"version": 4,
|
||||
"addr": "10.180.3.155"
|
||||
}
|
||||
]
|
||||
}
|
||||
}`)
|
||||
})
|
||||
}
|
||||
|
||||
// ListNetworkAddressesExpected represents an expected repsonse from a ListAddressesByNetwork request.
|
||||
var ListNetworkAddressesExpected = []Address{
|
||||
Address{
|
||||
Version: 4,
|
||||
Address: "50.56.176.35",
|
||||
},
|
||||
Address{
|
||||
Version: 6,
|
||||
Address: "2001:4800:780e:510:be76:4eff:fe04:84a8",
|
||||
},
|
||||
}
|
||||
|
||||
// HandleNetworkAddressListSuccessfully sets up the test server to respond to a ListAddressesByNetwork request.
|
||||
func HandleNetworkAddressListSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/servers/asdfasdfasdf/ips/public", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
fmt.Fprintf(w, `{
|
||||
"public": [
|
||||
{
|
||||
"version": 4,
|
||||
"addr": "50.56.176.35"
|
||||
},
|
||||
{
|
||||
"version": 6,
|
||||
"addr": "2001:4800:780e:510:be76:4eff:fe04:84a8"
|
||||
}
|
||||
]
|
||||
}`)
|
||||
})
|
||||
}
|
||||
|
||||
// HandleCreateServerImageSuccessfully sets up the test server to respond to a TestCreateServerImage request.
|
||||
func HandleCreateServerImageSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/servers/serverimage/action", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
w.Header().Add("Location", "https://0.0.0.0/images/xxxx-xxxxx-xxxxx-xxxx")
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
})
|
||||
}
|
||||
|
||||
// HandlePasswordGetSuccessfully sets up the test server to respond to a password Get request.
|
||||
func HandlePasswordGetSuccessfully(t *testing.T) {
|
||||
th.Mux.HandleFunc("/servers/1234asdf/os-server-password", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
|
||||
fmt.Fprintf(w, ServerPasswordBody)
|
||||
})
|
||||
}
|
872
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/requests.go
generated
vendored
Normal file
872
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/requests.go
generated
vendored
Normal file
@@ -0,0 +1,872 @@
|
||||
package servers
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/openstack/compute/v2/flavors"
|
||||
"github.com/rackspace/gophercloud/openstack/compute/v2/images"
|
||||
"github.com/rackspace/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 {
|
||||
// A time/date stamp for when the server last changed status.
|
||||
ChangesSince string `q:"changes-since"`
|
||||
|
||||
// Name of the image in URL format.
|
||||
Image string `q:"image"`
|
||||
|
||||
// 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"`
|
||||
|
||||
// Value of the status of the server so that you can filter on "ACTIVE" for example.
|
||||
Status string `q:"status"`
|
||||
|
||||
// Name of the host as a string.
|
||||
Host string `q:"host"`
|
||||
|
||||
// UUID of the server at which you want to set a marker.
|
||||
Marker string `q:"marker"`
|
||||
|
||||
// Integer value for the limit of values to return.
|
||||
Limit int `q:"limit"`
|
||||
|
||||
// Bool to show all tenants
|
||||
AllTenants bool `q:"all_tenants"`
|
||||
}
|
||||
|
||||
// ToServerListQuery formats a ListOpts into a query string.
|
||||
func (opts ListOpts) ToServerListQuery() (string, error) {
|
||||
q, err := gophercloud.BuildQueryString(opts)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return q.String(), nil
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
createPageFn := func(r pagination.PageResult) pagination.Page {
|
||||
return ServerPage{pagination.LinkedPageBase{PageResult: r}}
|
||||
}
|
||||
|
||||
return pagination.NewPager(client, url, createPageFn)
|
||||
}
|
||||
|
||||
// CreateOptsBuilder describes struct types that can be accepted by the Create call.
|
||||
// The CreateOpts struct in this package does.
|
||||
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 nova-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 [optional] 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 [required] is the name to assign to the newly launched server.
|
||||
Name string
|
||||
|
||||
// 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
|
||||
|
||||
// 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
|
||||
|
||||
// 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
|
||||
|
||||
// FlavorName [optional; required if FlavorRef is not provided] is the name of
|
||||
// the flavor that describes the server's specs.
|
||||
FlavorName string
|
||||
|
||||
// SecurityGroups [optional] lists the names of the security groups to which this server should belong.
|
||||
SecurityGroups []string
|
||||
|
||||
// UserData [optional] contains configuration information or scripts to use upon launch.
|
||||
// Create will base64-encode it for you.
|
||||
UserData []byte
|
||||
|
||||
// AvailabilityZone [optional] in which to launch the server.
|
||||
AvailabilityZone string
|
||||
|
||||
// Networks [optional] 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
|
||||
|
||||
// Metadata [optional] contains key-value pairs (up to 255 bytes each) to attach to the server.
|
||||
Metadata map[string]string
|
||||
|
||||
// Personality [optional] includes files to inject into the server at launch.
|
||||
// Create will base64-encode file contents for you.
|
||||
Personality Personality
|
||||
|
||||
// ConfigDrive [optional] enables metadata injection through a configuration drive.
|
||||
ConfigDrive bool
|
||||
|
||||
// AdminPass [optional] sets the root user password. If not set, a randomly-generated
|
||||
// password will be created and returned in the response.
|
||||
AdminPass string
|
||||
|
||||
// AccessIPv4 [optional] specifies an IPv4 address for the instance.
|
||||
AccessIPv4 string
|
||||
|
||||
// AccessIPv6 [optional] specifies an IPv6 address for the instance.
|
||||
AccessIPv6 string
|
||||
}
|
||||
|
||||
// ToServerCreateMap assembles a request body based on the contents of a CreateOpts.
|
||||
func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) {
|
||||
server := make(map[string]interface{})
|
||||
|
||||
server["name"] = opts.Name
|
||||
server["imageRef"] = opts.ImageRef
|
||||
server["imageName"] = opts.ImageName
|
||||
server["flavorRef"] = opts.FlavorRef
|
||||
server["flavorName"] = opts.FlavorName
|
||||
|
||||
if opts.UserData != nil {
|
||||
encoded := base64.StdEncoding.EncodeToString(opts.UserData)
|
||||
server["user_data"] = &encoded
|
||||
}
|
||||
if opts.ConfigDrive {
|
||||
server["config_drive"] = "true"
|
||||
}
|
||||
if opts.AvailabilityZone != "" {
|
||||
server["availability_zone"] = opts.AvailabilityZone
|
||||
}
|
||||
if opts.Metadata != nil {
|
||||
server["metadata"] = opts.Metadata
|
||||
}
|
||||
if opts.AdminPass != "" {
|
||||
server["adminPass"] = opts.AdminPass
|
||||
}
|
||||
if opts.AccessIPv4 != "" {
|
||||
server["accessIPv4"] = opts.AccessIPv4
|
||||
}
|
||||
if opts.AccessIPv6 != "" {
|
||||
server["accessIPv6"] = opts.AccessIPv6
|
||||
}
|
||||
|
||||
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}
|
||||
}
|
||||
server["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
|
||||
}
|
||||
}
|
||||
server["networks"] = networks
|
||||
}
|
||||
|
||||
if len(opts.Personality) > 0 {
|
||||
server["personality"] = opts.Personality
|
||||
}
|
||||
|
||||
return map[string]interface{}{"server": server}, nil
|
||||
}
|
||||
|
||||
// Create requests a server to be provisioned to the user in the current tenant.
|
||||
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
|
||||
var res CreateResult
|
||||
|
||||
reqBody, err := opts.ToServerCreateMap()
|
||||
if err != nil {
|
||||
res.Err = err
|
||||
return res
|
||||
}
|
||||
|
||||
// If ImageRef isn't provided, use ImageName to ascertain the image ID.
|
||||
if reqBody["server"].(map[string]interface{})["imageRef"].(string) == "" {
|
||||
imageName := reqBody["server"].(map[string]interface{})["imageName"].(string)
|
||||
if imageName == "" {
|
||||
res.Err = errors.New("One and only one of ImageRef and ImageName must be provided.")
|
||||
return res
|
||||
}
|
||||
imageID, err := images.IDFromName(client, imageName)
|
||||
if err != nil {
|
||||
res.Err = err
|
||||
return res
|
||||
}
|
||||
reqBody["server"].(map[string]interface{})["imageRef"] = imageID
|
||||
}
|
||||
delete(reqBody["server"].(map[string]interface{}), "imageName")
|
||||
|
||||
// If FlavorRef isn't provided, use FlavorName to ascertain the flavor ID.
|
||||
if reqBody["server"].(map[string]interface{})["flavorRef"].(string) == "" {
|
||||
flavorName := reqBody["server"].(map[string]interface{})["flavorName"].(string)
|
||||
if flavorName == "" {
|
||||
res.Err = errors.New("One and only one of FlavorRef and FlavorName must be provided.")
|
||||
return res
|
||||
}
|
||||
flavorID, err := flavors.IDFromName(client, flavorName)
|
||||
if err != nil {
|
||||
res.Err = err
|
||||
return res
|
||||
}
|
||||
reqBody["server"].(map[string]interface{})["flavorRef"] = flavorID
|
||||
}
|
||||
delete(reqBody["server"].(map[string]interface{}), "flavorName")
|
||||
|
||||
_, res.Err = client.Post(listURL(client), reqBody, &res.Body, nil)
|
||||
return res
|
||||
}
|
||||
|
||||
// Delete requests that a server previously provisioned be removed from your account.
|
||||
func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
|
||||
var res DeleteResult
|
||||
_, res.Err = client.Delete(deleteURL(client, id), nil)
|
||||
return res
|
||||
}
|
||||
|
||||
func ForceDelete(client *gophercloud.ServiceClient, id string) ActionResult {
|
||||
var req struct {
|
||||
ForceDelete string `json:"forceDelete"`
|
||||
}
|
||||
|
||||
var res ActionResult
|
||||
_, res.Err = client.Post(actionURL(client, id), req, nil, nil)
|
||||
return res
|
||||
|
||||
}
|
||||
|
||||
// Get requests details on a single server, by ID.
|
||||
func Get(client *gophercloud.ServiceClient, id string) GetResult {
|
||||
var result GetResult
|
||||
_, result.Err = client.Get(getURL(client, id), &result.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200, 203},
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
// UpdateOptsBuilder allows extensions to add additional attributes to the Update request.
|
||||
type UpdateOptsBuilder interface {
|
||||
ToServerUpdateMap() map[string]interface{}
|
||||
}
|
||||
|
||||
// UpdateOpts specifies the base attributes that may be updated on an existing server.
|
||||
type UpdateOpts struct {
|
||||
// Name [optional] 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
|
||||
|
||||
// AccessIPv4 [optional] provides a new IPv4 address for the instance.
|
||||
AccessIPv4 string
|
||||
|
||||
// AccessIPv6 [optional] provides a new IPv6 address for the instance.
|
||||
AccessIPv6 string
|
||||
}
|
||||
|
||||
// ToServerUpdateMap formats an UpdateOpts structure into a request body.
|
||||
func (opts UpdateOpts) ToServerUpdateMap() map[string]interface{} {
|
||||
server := make(map[string]string)
|
||||
if opts.Name != "" {
|
||||
server["name"] = opts.Name
|
||||
}
|
||||
if opts.AccessIPv4 != "" {
|
||||
server["accessIPv4"] = opts.AccessIPv4
|
||||
}
|
||||
if opts.AccessIPv6 != "" {
|
||||
server["accessIPv6"] = opts.AccessIPv6
|
||||
}
|
||||
return map[string]interface{}{"server": server}
|
||||
}
|
||||
|
||||
// Update requests that various attributes of the indicated server be changed.
|
||||
func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
|
||||
var result UpdateResult
|
||||
reqBody := opts.ToServerUpdateMap()
|
||||
_, result.Err = client.Put(updateURL(client, id), reqBody, &result.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200},
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
// ChangeAdminPassword alters the administrator or root password for a specified server.
|
||||
func ChangeAdminPassword(client *gophercloud.ServiceClient, id, newPassword string) ActionResult {
|
||||
var req struct {
|
||||
ChangePassword struct {
|
||||
AdminPass string `json:"adminPass"`
|
||||
} `json:"changePassword"`
|
||||
}
|
||||
|
||||
req.ChangePassword.AdminPass = newPassword
|
||||
|
||||
var res ActionResult
|
||||
_, res.Err = client.Post(actionURL(client, id), req, nil, nil)
|
||||
return res
|
||||
}
|
||||
|
||||
// ErrArgument errors occur when an argument supplied to a package function
|
||||
// fails to fall within acceptable values. For example, the Reboot() function
|
||||
// expects the "how" parameter to be one of HardReboot or SoftReboot. These
|
||||
// constants are (currently) strings, leading someone to wonder if they can pass
|
||||
// other string values instead, perhaps in an effort to break the API of their
|
||||
// provider. Reboot() returns this error in this situation.
|
||||
//
|
||||
// Function identifies which function was called/which function is generating
|
||||
// the error.
|
||||
// Argument identifies which formal argument was responsible for producing the
|
||||
// error.
|
||||
// Value provides the value as it was passed into the function.
|
||||
type ErrArgument struct {
|
||||
Function, Argument string
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
// Error yields a useful diagnostic for debugging purposes.
|
||||
func (e *ErrArgument) Error() string {
|
||||
return fmt.Sprintf("Bad argument in call to %s, formal parameter %s, value %#v", e.Function, e.Argument, e.Value)
|
||||
}
|
||||
|
||||
func (e *ErrArgument) String() string {
|
||||
return e.Error()
|
||||
}
|
||||
|
||||
// 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
|
||||
)
|
||||
|
||||
// Reboot requests that a given server reboot.
|
||||
// Two methods exist for rebooting a server:
|
||||
//
|
||||
// HardReboot (aka PowerCycle) restarts 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 restored or the VM instance restarted.
|
||||
//
|
||||
// SoftReboot (aka OSReboot) simply tells the OS to restart under its own procedures.
|
||||
// E.g., in Linux, asking it to enter runlevel 6, or executing "sudo shutdown -r now", or by asking Windows to restart the machine.
|
||||
func Reboot(client *gophercloud.ServiceClient, id string, how RebootMethod) ActionResult {
|
||||
var res ActionResult
|
||||
|
||||
if (how != SoftReboot) && (how != HardReboot) {
|
||||
res.Err = &ErrArgument{
|
||||
Function: "Reboot",
|
||||
Argument: "how",
|
||||
Value: how,
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
reqBody := struct {
|
||||
C map[string]string `json:"reboot"`
|
||||
}{
|
||||
map[string]string{"type": string(how)},
|
||||
}
|
||||
|
||||
_, res.Err = client.Post(actionURL(client, id), reqBody, nil, nil)
|
||||
return res
|
||||
}
|
||||
|
||||
// RebuildOptsBuilder is an interface that allows extensions to override the
|
||||
// default behaviour of rebuild options
|
||||
type RebuildOptsBuilder interface {
|
||||
ToServerRebuildMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// RebuildOpts represents the configuration options used in a server rebuild
|
||||
// operation
|
||||
type RebuildOpts struct {
|
||||
// Required. The ID of the image you want your server to be provisioned on
|
||||
ImageID string
|
||||
|
||||
// Name to set the server to
|
||||
Name string
|
||||
|
||||
// Required. The server's admin password
|
||||
AdminPass string
|
||||
|
||||
// AccessIPv4 [optional] provides a new IPv4 address for the instance.
|
||||
AccessIPv4 string
|
||||
|
||||
// AccessIPv6 [optional] provides a new IPv6 address for the instance.
|
||||
AccessIPv6 string
|
||||
|
||||
// Metadata [optional] contains key-value pairs (up to 255 bytes each) to attach to the server.
|
||||
Metadata map[string]string
|
||||
|
||||
// Personality [optional] includes files to inject into the server at launch.
|
||||
// Rebuild will base64-encode file contents for you.
|
||||
Personality Personality
|
||||
}
|
||||
|
||||
// ToServerRebuildMap formats a RebuildOpts struct into a map for use in JSON
|
||||
func (opts RebuildOpts) ToServerRebuildMap() (map[string]interface{}, error) {
|
||||
var err error
|
||||
server := make(map[string]interface{})
|
||||
|
||||
if opts.AdminPass == "" {
|
||||
err = fmt.Errorf("AdminPass is required")
|
||||
}
|
||||
|
||||
if opts.ImageID == "" {
|
||||
err = fmt.Errorf("ImageID is required")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return server, err
|
||||
}
|
||||
|
||||
server["name"] = opts.Name
|
||||
server["adminPass"] = opts.AdminPass
|
||||
server["imageRef"] = opts.ImageID
|
||||
|
||||
if opts.AccessIPv4 != "" {
|
||||
server["accessIPv4"] = opts.AccessIPv4
|
||||
}
|
||||
|
||||
if opts.AccessIPv6 != "" {
|
||||
server["accessIPv6"] = opts.AccessIPv6
|
||||
}
|
||||
|
||||
if opts.Metadata != nil {
|
||||
server["metadata"] = opts.Metadata
|
||||
}
|
||||
|
||||
if len(opts.Personality) > 0 {
|
||||
server["personality"] = opts.Personality
|
||||
}
|
||||
|
||||
return map[string]interface{}{"rebuild": server}, 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) RebuildResult {
|
||||
var result RebuildResult
|
||||
|
||||
if id == "" {
|
||||
result.Err = fmt.Errorf("ID is required")
|
||||
return result
|
||||
}
|
||||
|
||||
reqBody, err := opts.ToServerRebuildMap()
|
||||
if err != nil {
|
||||
result.Err = err
|
||||
return result
|
||||
}
|
||||
|
||||
_, result.Err = client.Post(actionURL(client, id), reqBody, &result.Body, nil)
|
||||
return result
|
||||
}
|
||||
|
||||
// ResizeOptsBuilder is an interface that allows extensions to override the default structure of
|
||||
// a 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
|
||||
}
|
||||
|
||||
// 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) {
|
||||
resize := map[string]interface{}{
|
||||
"flavorRef": opts.FlavorRef,
|
||||
}
|
||||
|
||||
return map[string]interface{}{"resize": resize}, nil
|
||||
}
|
||||
|
||||
// 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 RESIZE_VERIFY 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) ActionResult {
|
||||
var res ActionResult
|
||||
reqBody, err := opts.ToServerResizeMap()
|
||||
if err != nil {
|
||||
res.Err = err
|
||||
return res
|
||||
}
|
||||
|
||||
_, res.Err = client.Post(actionURL(client, id), reqBody, nil, nil)
|
||||
return res
|
||||
}
|
||||
|
||||
// ConfirmResize confirms a previous resize operation on a server.
|
||||
// See Resize() for more details.
|
||||
func ConfirmResize(client *gophercloud.ServiceClient, id string) ActionResult {
|
||||
var res ActionResult
|
||||
|
||||
reqBody := map[string]interface{}{"confirmResize": nil}
|
||||
_, res.Err = client.Post(actionURL(client, id), reqBody, nil, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{201, 202, 204},
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
// RevertResize cancels a previous resize operation on a server.
|
||||
// See Resize() for more details.
|
||||
func RevertResize(client *gophercloud.ServiceClient, id string) ActionResult {
|
||||
var res ActionResult
|
||||
reqBody := map[string]interface{}{"revertResize": nil}
|
||||
_, res.Err = client.Post(actionURL(client, id), reqBody, nil, nil)
|
||||
return res
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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) {
|
||||
server := make(map[string]interface{})
|
||||
if opts.AdminPass != "" {
|
||||
server["adminPass"] = opts.AdminPass
|
||||
}
|
||||
return map[string]interface{}{"rescue": server}, nil
|
||||
}
|
||||
|
||||
// Rescue instructs the provider to place the server into RESCUE mode.
|
||||
func Rescue(client *gophercloud.ServiceClient, id string, opts RescueOptsBuilder) RescueResult {
|
||||
var result RescueResult
|
||||
|
||||
if id == "" {
|
||||
result.Err = fmt.Errorf("ID is required")
|
||||
return result
|
||||
}
|
||||
reqBody, err := opts.ToServerRescueMap()
|
||||
if err != nil {
|
||||
result.Err = err
|
||||
return result
|
||||
}
|
||||
|
||||
_, result.Err = client.Post(actionURL(client, id), reqBody, &result.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200},
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// 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) ResetMetadataResult {
|
||||
var res ResetMetadataResult
|
||||
metadata, err := opts.ToMetadataResetMap()
|
||||
if err != nil {
|
||||
res.Err = err
|
||||
return res
|
||||
}
|
||||
_, res.Err = client.Put(metadataURL(client, id), metadata, &res.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200},
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
// Metadata requests all the metadata for the given server ID.
|
||||
func Metadata(client *gophercloud.ServiceClient, id string) GetMetadataResult {
|
||||
var res GetMetadataResult
|
||||
_, res.Err = client.Get(metadataURL(client, id), &res.Body, nil)
|
||||
return res
|
||||
}
|
||||
|
||||
// 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) UpdateMetadataResult {
|
||||
var res UpdateMetadataResult
|
||||
metadata, err := opts.ToMetadataUpdateMap()
|
||||
if err != nil {
|
||||
res.Err = err
|
||||
return res
|
||||
}
|
||||
_, res.Err = client.Post(metadataURL(client, id), metadata, &res.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200},
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return nil, "", errors.New("CreateMetadatum operation must have 1 and only 1 key-value pair.")
|
||||
}
|
||||
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) CreateMetadatumResult {
|
||||
var res CreateMetadatumResult
|
||||
metadatum, key, err := opts.ToMetadatumCreateMap()
|
||||
if err != nil {
|
||||
res.Err = err
|
||||
return res
|
||||
}
|
||||
|
||||
_, res.Err = client.Put(metadatumURL(client, id, key), metadatum, &res.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200},
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
// Metadatum requests the key-value pair with the given key for the given server ID.
|
||||
func Metadatum(client *gophercloud.ServiceClient, id, key string) GetMetadatumResult {
|
||||
var res GetMetadatumResult
|
||||
_, res.Err = client.Request("GET", metadatumURL(client, id, key), gophercloud.RequestOpts{
|
||||
JSONResponse: &res.Body,
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
// DeleteMetadatum will delete the key-value pair with the given key for the given server ID.
|
||||
func DeleteMetadatum(client *gophercloud.ServiceClient, id, key string) DeleteMetadatumResult {
|
||||
var res DeleteMetadatumResult
|
||||
_, res.Err = client.Delete(metadatumURL(client, id, key), nil)
|
||||
return res
|
||||
}
|
||||
|
||||
// ListAddresses makes a request against the API to list the servers IP addresses.
|
||||
func ListAddresses(client *gophercloud.ServiceClient, id string) pagination.Pager {
|
||||
createPageFn := func(r pagination.PageResult) pagination.Page {
|
||||
return AddressPage{pagination.SinglePageBase(r)}
|
||||
}
|
||||
return pagination.NewPager(client, listAddressesURL(client, id), createPageFn)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
createPageFn := func(r pagination.PageResult) pagination.Page {
|
||||
return NetworkAddressPage{pagination.SinglePageBase(r)}
|
||||
}
|
||||
return pagination.NewPager(client, listAddressesByNetworkURL(client, id, network), createPageFn)
|
||||
}
|
||||
|
||||
type CreateImageOpts struct {
|
||||
// Name [required] of the image/snapshot
|
||||
Name string
|
||||
// Metadata [optional] contains key-value pairs (up to 255 bytes each) to attach to the created image.
|
||||
Metadata map[string]string
|
||||
}
|
||||
|
||||
type CreateImageOptsBuilder interface {
|
||||
ToServerCreateImageMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// ToServerCreateImageMap formats a CreateImageOpts structure into a request body.
|
||||
func (opts CreateImageOpts) ToServerCreateImageMap() (map[string]interface{}, error) {
|
||||
var err error
|
||||
img := make(map[string]interface{})
|
||||
if opts.Name == "" {
|
||||
return nil, fmt.Errorf("Cannot create a server image without a name")
|
||||
}
|
||||
img["name"] = opts.Name
|
||||
if opts.Metadata != nil {
|
||||
img["metadata"] = opts.Metadata
|
||||
}
|
||||
createImage := make(map[string]interface{})
|
||||
createImage["createImage"] = img
|
||||
return createImage, err
|
||||
}
|
||||
|
||||
// CreateImage makes a request against the nova API to schedule an image to be created of the server
|
||||
func CreateImage(client *gophercloud.ServiceClient, serverId string, opts CreateImageOptsBuilder) CreateImageResult {
|
||||
var res CreateImageResult
|
||||
reqBody, err := opts.ToServerCreateImageMap()
|
||||
if err != nil {
|
||||
res.Err = err
|
||||
return res
|
||||
}
|
||||
response, err := client.Post(actionURL(client, serverId), reqBody, nil, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{202},
|
||||
})
|
||||
res.Err = err
|
||||
res.Header = response.Header
|
||||
return res
|
||||
}
|
||||
|
||||
// IDFromName is a convienience function that returns a server's ID given its name.
|
||||
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
|
||||
serverCount := 0
|
||||
serverID := ""
|
||||
if name == "" {
|
||||
return "", fmt.Errorf("A server name must be provided.")
|
||||
}
|
||||
pager := List(client, nil)
|
||||
pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
serverList, err := ExtractServers(page)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, s := range serverList {
|
||||
if s.Name == name {
|
||||
serverCount++
|
||||
serverID = s.ID
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
|
||||
switch serverCount {
|
||||
case 0:
|
||||
return "", fmt.Errorf("Unable to find server: %s", name)
|
||||
case 1:
|
||||
return serverID, nil
|
||||
default:
|
||||
return "", fmt.Errorf("Found %d servers matching %s", serverCount, name)
|
||||
}
|
||||
}
|
||||
|
||||
// GetPassword makes a request against the nova API to get the encrypted administrative password.
|
||||
func GetPassword(client *gophercloud.ServiceClient, serverId string) GetPasswordResult {
|
||||
var res GetPasswordResult
|
||||
_, res.Err = client.Request("GET", passwordURL(client, serverId), gophercloud.RequestOpts{
|
||||
JSONResponse: &res.Body,
|
||||
})
|
||||
return res
|
||||
}
|
415
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/results.go
generated
vendored
Normal file
415
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/results.go
generated
vendored
Normal file
@@ -0,0 +1,415 @@
|
||||
package servers
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"path"
|
||||
"reflect"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
type serverResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// Extract interprets any serverResult as a Server, if possible.
|
||||
func (r serverResult) Extract() (*Server, error) {
|
||||
if r.Err != nil {
|
||||
return nil, r.Err
|
||||
}
|
||||
|
||||
var response struct {
|
||||
Server Server `mapstructure:"server"`
|
||||
}
|
||||
|
||||
config := &mapstructure.DecoderConfig{
|
||||
DecodeHook: toMapFromString,
|
||||
Result: &response,
|
||||
}
|
||||
decoder, err := mapstructure.NewDecoder(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = decoder.Decode(r.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &response.Server, nil
|
||||
}
|
||||
|
||||
// CreateResult temporarily contains the response from a Create call.
|
||||
type CreateResult struct {
|
||||
serverResult
|
||||
}
|
||||
|
||||
// GetResult temporarily contains the response from a Get call.
|
||||
type GetResult struct {
|
||||
serverResult
|
||||
}
|
||||
|
||||
// UpdateResult temporarily contains the response from an Update call.
|
||||
type UpdateResult struct {
|
||||
serverResult
|
||||
}
|
||||
|
||||
// DeleteResult temporarily contains the response from a Delete call.
|
||||
type DeleteResult struct {
|
||||
gophercloud.ErrResult
|
||||
}
|
||||
|
||||
// RebuildResult temporarily contains the response from a Rebuild call.
|
||||
type RebuildResult struct {
|
||||
serverResult
|
||||
}
|
||||
|
||||
// ActionResult represents the result of server action operations, like reboot
|
||||
type ActionResult struct {
|
||||
gophercloud.ErrResult
|
||||
}
|
||||
|
||||
// RescueResult represents the result of a server rescue operation
|
||||
type RescueResult struct {
|
||||
ActionResult
|
||||
}
|
||||
|
||||
// CreateImageResult represents the result of an image creation operation
|
||||
type CreateImageResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// GetPasswordResult represent the result of a get os-server-password operation.
|
||||
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) {
|
||||
|
||||
if r.Err != nil {
|
||||
return "", r.Err
|
||||
}
|
||||
|
||||
var response struct {
|
||||
Password string `mapstructure:"password"`
|
||||
}
|
||||
|
||||
err := mapstructure.Decode(r.Body, &response)
|
||||
if err == nil && privateKey != nil && response.Password != "" {
|
||||
return decryptPassword(response.Password, privateKey)
|
||||
}
|
||||
return response.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 (res CreateImageResult) ExtractImageID() (string, error) {
|
||||
if res.Err != nil {
|
||||
return "", res.Err
|
||||
}
|
||||
// Get the image id from the header
|
||||
u, err := url.ParseRequestURI(res.Header.Get("Location"))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to parse the image id: %s", err.Error())
|
||||
}
|
||||
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) {
|
||||
if r.Err != nil {
|
||||
return "", r.Err
|
||||
}
|
||||
|
||||
var response struct {
|
||||
AdminPass string `mapstructure:"adminPass"`
|
||||
}
|
||||
|
||||
err := mapstructure.Decode(r.Body, &response)
|
||||
return response.AdminPass, err
|
||||
}
|
||||
|
||||
// Server exposes only the standard OpenStack fields corresponding to a given server on the user's account.
|
||||
type Server struct {
|
||||
// ID uniquely identifies this server amongst all other servers, including those not accessible to the current tenant.
|
||||
ID string
|
||||
|
||||
// TenantID identifies the tenant owning this server resource.
|
||||
TenantID string `mapstructure:"tenant_id"`
|
||||
|
||||
// UserID uniquely identifies the user account owning the tenant.
|
||||
UserID string `mapstructure:"user_id"`
|
||||
|
||||
// Name contains the human-readable name for the server.
|
||||
Name string
|
||||
|
||||
// Updated and Created contain ISO-8601 timestamps of when the state of the server last changed, and when it was created.
|
||||
Updated string
|
||||
Created string
|
||||
|
||||
HostID string
|
||||
|
||||
// Status contains the current operational status of the server, such as IN_PROGRESS or ACTIVE.
|
||||
Status string
|
||||
|
||||
// Progress ranges from 0..100.
|
||||
// A request made against the server completes only once Progress reaches 100.
|
||||
Progress int
|
||||
|
||||
// AccessIPv4 and AccessIPv6 contain the IP addresses of the server, suitable for remote access for administration.
|
||||
AccessIPv4, AccessIPv6 string
|
||||
|
||||
// Image refers to a JSON object, which itself indicates the OS image used to deploy the server.
|
||||
Image map[string]interface{}
|
||||
|
||||
// Flavor refers to a JSON object, which itself indicates the hardware configuration of the deployed server.
|
||||
Flavor map[string]interface{}
|
||||
|
||||
// Addresses includes a list of all IP addresses assigned to the server, keyed by pool.
|
||||
Addresses map[string]interface{}
|
||||
|
||||
// Metadata includes a list of all user-specified key-value pairs attached to the server.
|
||||
Metadata map[string]interface{}
|
||||
|
||||
// Links includes HTTP references to the itself, useful for passing along to other APIs that might want a server reference.
|
||||
Links []interface{}
|
||||
|
||||
// KeyName indicates which public key was injected into the server on launch.
|
||||
KeyName string `json:"key_name" mapstructure:"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" mapstructure:"adminPass"`
|
||||
|
||||
// SecurityGroups includes the security groups that this instance has applied to it
|
||||
SecurityGroups []map[string]interface{} `json:"security_groups" mapstructure:"security_groups"`
|
||||
}
|
||||
|
||||
// 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 (page ServerPage) IsEmpty() (bool, error) {
|
||||
servers, err := ExtractServers(page)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
return len(servers) == 0, nil
|
||||
}
|
||||
|
||||
// NextPageURL uses the response's embedded link reference to navigate to the next page of results.
|
||||
func (page ServerPage) NextPageURL() (string, error) {
|
||||
type resp struct {
|
||||
Links []gophercloud.Link `mapstructure:"servers_links"`
|
||||
}
|
||||
|
||||
var r resp
|
||||
err := mapstructure.Decode(page.Body, &r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return gophercloud.ExtractNextURL(r.Links)
|
||||
}
|
||||
|
||||
// ExtractServers interprets the results of a single page from a List() call, producing a slice of Server entities.
|
||||
func ExtractServers(page pagination.Page) ([]Server, error) {
|
||||
casted := page.(ServerPage).Body
|
||||
|
||||
var response struct {
|
||||
Servers []Server `mapstructure:"servers"`
|
||||
}
|
||||
|
||||
config := &mapstructure.DecoderConfig{
|
||||
DecodeHook: toMapFromString,
|
||||
Result: &response,
|
||||
}
|
||||
decoder, err := mapstructure.NewDecoder(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = decoder.Decode(casted)
|
||||
|
||||
return response.Servers, err
|
||||
}
|
||||
|
||||
// MetadataResult contains the result of a call for (potentially) multiple key-value pairs.
|
||||
type MetadataResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// GetMetadataResult temporarily contains the response from a metadata Get call.
|
||||
type GetMetadataResult struct {
|
||||
MetadataResult
|
||||
}
|
||||
|
||||
// ResetMetadataResult temporarily contains the response from a metadata Reset call.
|
||||
type ResetMetadataResult struct {
|
||||
MetadataResult
|
||||
}
|
||||
|
||||
// UpdateMetadataResult temporarily contains the response from a metadata Update call.
|
||||
type UpdateMetadataResult struct {
|
||||
MetadataResult
|
||||
}
|
||||
|
||||
// MetadatumResult contains the result of a call for individual a single key-value pair.
|
||||
type MetadatumResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// GetMetadatumResult temporarily contains the response from a metadatum Get call.
|
||||
type GetMetadatumResult struct {
|
||||
MetadatumResult
|
||||
}
|
||||
|
||||
// CreateMetadatumResult temporarily contains the response from a metadatum Create call.
|
||||
type CreateMetadatumResult struct {
|
||||
MetadatumResult
|
||||
}
|
||||
|
||||
// DeleteMetadatumResult temporarily contains the response from a metadatum Delete call.
|
||||
type DeleteMetadatumResult struct {
|
||||
gophercloud.ErrResult
|
||||
}
|
||||
|
||||
// Extract interprets any MetadataResult as a Metadata, if possible.
|
||||
func (r MetadataResult) Extract() (map[string]string, error) {
|
||||
if r.Err != nil {
|
||||
return nil, r.Err
|
||||
}
|
||||
|
||||
var response struct {
|
||||
Metadata map[string]string `mapstructure:"metadata"`
|
||||
}
|
||||
|
||||
err := mapstructure.Decode(r.Body, &response)
|
||||
return response.Metadata, err
|
||||
}
|
||||
|
||||
// Extract interprets any MetadatumResult as a Metadatum, if possible.
|
||||
func (r MetadatumResult) Extract() (map[string]string, error) {
|
||||
if r.Err != nil {
|
||||
return nil, r.Err
|
||||
}
|
||||
|
||||
var response struct {
|
||||
Metadatum map[string]string `mapstructure:"meta"`
|
||||
}
|
||||
|
||||
err := mapstructure.Decode(r.Body, &response)
|
||||
return response.Metadatum, err
|
||||
}
|
||||
|
||||
func toMapFromString(from reflect.Kind, to reflect.Kind, data interface{}) (interface{}, error) {
|
||||
if (from == reflect.String) && (to == reflect.Map) {
|
||||
return map[string]interface{}{}, nil
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// Address represents an IP address.
|
||||
type Address struct {
|
||||
Version int `mapstructure:"version"`
|
||||
Address string `mapstructure:"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)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
return len(addresses) == 0, nil
|
||||
}
|
||||
|
||||
// ExtractAddresses interprets the results of a single page from a ListAddresses() call,
|
||||
// producing a map of addresses.
|
||||
func ExtractAddresses(page pagination.Page) (map[string][]Address, error) {
|
||||
casted := page.(AddressPage).Body
|
||||
|
||||
var response struct {
|
||||
Addresses map[string][]Address `mapstructure:"addresses"`
|
||||
}
|
||||
|
||||
err := mapstructure.Decode(casted, &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response.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)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
return len(addresses) == 0, nil
|
||||
}
|
||||
|
||||
// ExtractNetworkAddresses interprets the results of a single page from a ListAddressesByNetwork() call,
|
||||
// producing a slice of addresses.
|
||||
func ExtractNetworkAddresses(page pagination.Page) ([]Address, error) {
|
||||
casted := page.(NetworkAddressPage).Body
|
||||
|
||||
var response map[string][]Address
|
||||
err := mapstructure.Decode(casted, &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var key string
|
||||
for k := range response {
|
||||
key = k
|
||||
}
|
||||
|
||||
return response[key], err
|
||||
}
|
51
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/urls.go
generated
vendored
Normal file
51
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/urls.go
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
package servers
|
||||
|
||||
import "github.com/rackspace/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")
|
||||
}
|
20
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/util.go
generated
vendored
Normal file
20
vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/util.go
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
package servers
|
||||
|
||||
import "github.com/rackspace/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
|
||||
})
|
||||
}
|
Reference in New Issue
Block a user