// Copyright 2015 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.

// This file was initially autogenerated from the upstream wadl file, but has
// since been updated by hand.
// The WADLs were originally sourced from:
// https://github.com/openstack/api-site/tree/master/api-ref/src/wadls/volume-api/src/v2

package cinder

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
)

// RequestHandlerFn specifies a function signature which wadl2go will
// use to process the http.Request. What this means is up to the
// implementor.
type RequestHandlerFn func(*http.Request) (*http.Response, error)

type UpdateVolumeTypeParams struct {

	// VolumeType is required.
	//
	// A volume type offers a way to categorize or group volumes.
	VolumeType string `json:"volume_type"`

	//
	// The unique identifier of the tenant or account.
	TenantId string `json:"-"`

	// VolumeTypeId is required.
	//
	// The unique identifier for an existing volume type.
	VolumeTypeId string `json:"-"`
}

type VolumeType struct {
	ExtraSpecs struct {
		Capabilities string `json:"capabilities"`
	} `json:"extra_specs"`
	ID   string `json:"id"`
	Name string `json:"name"`
}

type UpdateVolumeTypeResults struct {
	VolumeType VolumeType `json:"volume_type"`
}

//
// Updates a volume type.
func updateVolumeType(client *Client, args UpdateVolumeTypeParams) (*UpdateVolumeTypeResults, error) {

	argsAsJson, err := json.Marshal(args)
	if err != nil {
		return nil, err
	}

	urlPath := url.URL{Path: fmt.Sprintf("types/%s", args.VolumeTypeId)}
	url := client.endpoint.ResolveReference(&urlPath).String()

	var req *http.Request
	if string(argsAsJson) != "{}" {
		req, err = http.NewRequest("PUT", url, bytes.NewBuffer(argsAsJson))
		if err != nil {
			return nil, err
		}
		req.Header.Set("Content-Type", "application/json")
	} else {
		req, err = http.NewRequest("PUT", url, nil)
		if err != nil {
			return nil, err
		}
	}

	resp, err := client.handleRequest(req)
	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	switch resp.StatusCode {
	default:
		return nil, fmt.Errorf("invalid status (%d): %s", resp.StatusCode, body)
	case 200:
		break
	}

	var results UpdateVolumeTypeResults
	json.Unmarshal(body, &results)

	return &results, nil
}

type UpdateVolumeTypeExtraSpecsParams struct {

	// VolumeType is required.
	//
	// A volume type offers a way to categorize or group volumes.
	VolumeType string `json:"volume_type"`

	// ExtraSpecs is required.
	//
	// A key:value pair that offers a way to show additional specifications associated with the volume type. Examples include capabilities, capacity, compression, and so on, depending on the storage driver in use.
	ExtraSpecs string `json:"extra_specs"`

	//
	// The unique identifier of the tenant or account.
	TenantId string `json:"-"`

	// VolumeTypeId is required.
	//
	// The unique identifier for an existing volume type.
	VolumeTypeId string `json:"-"`
}

type UpdateVolumeTypeExtraSpecsResults struct {
	VolumeType VolumeType `json:"volume_type"`
}

//
// Updates the extra specifications assigned to a volume type.
func updateVolumeTypeExtraSpecs(client *Client, args UpdateVolumeTypeExtraSpecsParams) (*UpdateVolumeTypeExtraSpecsResults, error) {

	argsAsJson, err := json.Marshal(args)
	if err != nil {
		return nil, err
	}

	urlPath := url.URL{Path: fmt.Sprintf("types/%s", args.VolumeTypeId)}
	url := client.endpoint.ResolveReference(&urlPath).String()

	var req *http.Request
	if string(argsAsJson) != "{}" {
		req, err = http.NewRequest("PUT", url, bytes.NewBuffer(argsAsJson))
		if err != nil {
			return nil, err
		}
		req.Header.Set("Content-Type", "application/json")
	} else {
		req, err = http.NewRequest("PUT", url, nil)
		if err != nil {
			return nil, err
		}
	}

	resp, err := client.handleRequest(req)
	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	switch resp.StatusCode {
	default:
		return nil, fmt.Errorf("invalid status (%d): %s", resp.StatusCode, body)
	case 200:
		break
	}

	var results UpdateVolumeTypeExtraSpecsResults
	json.Unmarshal(body, &results)

	return &results, nil
}

type GetSnapshotsSimpleParams struct {

	//
	// The unique identifier of the tenant or account.
	TenantId string `json:"-"`
}

type Snapshot struct {
	CreatedAt   string `json:"created_at"`
	Description string `json:"description"`
	ID          string `json:"id"`
	Metadata    struct {
		Key string `json:"key"`
	} `json:"metadata"`
	Name                                      string `json:"name"`
	Size                                      int    `json:"size"`
	Status                                    string `json:"status"`
	VolumeID                                  string `json:"volume_id"`
	Os_Extended_Snapshot_Attributes_Progress  string `json:"os-extended-snapshot-attributes:progress"`
	Os_Extended_Snapshot_Attributes_ProjectID string `json:"os-extended-snapshot-attributes:project_id"`
}

type GetSnapshotsSimpleResults struct {
	Snapshots []Snapshot `json:"snapshots"`
}

//
// Lists summary information for all Block Storage snapshots that the tenant who submits the request can access.
func getSnapshotsSimple(client *Client, args GetSnapshotsSimpleParams) (*GetSnapshotsSimpleResults, error) {

	argsAsJson, err := json.Marshal(args)
	if err != nil {
		return nil, err
	}

	urlPath := url.URL{Path: "snapshots"}
	url := client.endpoint.ResolveReference(&urlPath).String()

	var req *http.Request
	if string(argsAsJson) != "{}" {
		req, err = http.NewRequest("GET", url, bytes.NewBuffer(argsAsJson))
		if err != nil {
			return nil, err
		}
		req.Header.Set("Content-Type", "application/json")
	} else {
		req, err = http.NewRequest("GET", url, nil)
		if err != nil {
			return nil, err
		}
	}

	resp, err := client.handleRequest(req)
	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	switch resp.StatusCode {
	default:
		return nil, fmt.Errorf("invalid status (%d): %s", resp.StatusCode, body)
	case 200:
		break
	}

	var results GetSnapshotsSimpleResults
	json.Unmarshal(body, &results)

	return &results, nil
}

type UpdateSnapshotSnapshotParams struct {

	//  Describes the snapshot.
	Description string `json:"description,omitempty"`

	//  The name of the snapshot.
	Name string `json:"name,omitempty"`
}

type UpdateSnapshotParams struct {

	// Snapshot is required.

	Snapshot UpdateSnapshotSnapshotParams `json:"snapshot"`

	//
	// The unique identifier of the tenant or account.
	TenantId string `json:"-"`

	// SnapshotId is required.
	//
	// The unique identifier of an existing snapshot.
	SnapshotId string `json:"-"`
}

type UpdateSnapshotResults struct {
	Snapshot Snapshot `json:"snapshot"`
}

//
// Updates a specified snapshot.
func updateSnapshot(client *Client, args UpdateSnapshotParams) (*UpdateSnapshotResults, error) {

	argsAsJson, err := json.Marshal(args)
	if err != nil {
		return nil, err
	}

	urlPath := url.URL{Path: fmt.Sprintf("snapshots/%s", args.SnapshotId)}
	url := client.endpoint.ResolveReference(&urlPath).String()

	var req *http.Request
	if string(argsAsJson) != "{}" {
		req, err = http.NewRequest("PUT", url, bytes.NewBuffer(argsAsJson))
		if err != nil {
			return nil, err
		}
		req.Header.Set("Content-Type", "application/json")
	} else {
		req, err = http.NewRequest("PUT", url, nil)
		if err != nil {
			return nil, err
		}
	}

	resp, err := client.handleRequest(req)
	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	switch resp.StatusCode {
	default:
		return nil, fmt.Errorf("invalid status (%d): %s", resp.StatusCode, body)
	case 200:
		break
	}

	var results UpdateSnapshotResults
	json.Unmarshal(body, &results)

	return &results, nil
}

type ShowSnapshotMetadataParams struct {

	//
	// The unique identifier of the tenant or account.
	TenantId string `json:"-"`

	// SnapshotId is required.
	//
	// The unique identifier of an existing snapshot.
	SnapshotId string `json:"-"`
}

type ShowSnapshotMetadataResults struct {
	Snapshot Snapshot `json:"snapshot"`
}

//
// Shows the metadata for a specified snapshot.
func showSnapshotMetadata(client *Client, args ShowSnapshotMetadataParams) (*ShowSnapshotMetadataResults, error) {

	argsAsJson, err := json.Marshal(args)
	if err != nil {
		return nil, err
	}

	urlPath := url.URL{Path: fmt.Sprintf("snapshots/%s/metadata", args.SnapshotId)}
	url := client.endpoint.ResolveReference(&urlPath).String()

	var req *http.Request
	if string(argsAsJson) != "{}" {
		req, err = http.NewRequest("GET", url, bytes.NewBuffer(argsAsJson))
		if err != nil {
			return nil, err
		}
		req.Header.Set("Content-Type", "application/json")
	} else {
		req, err = http.NewRequest("GET", url, nil)
		if err != nil {
			return nil, err
		}
	}

	resp, err := client.handleRequest(req)
	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	switch resp.StatusCode {
	default:
		return nil, fmt.Errorf("invalid status (%d): %s", resp.StatusCode, body)
	case 200:
		break
	}

	var results ShowSnapshotMetadataResults
	json.Unmarshal(body, &results)

	return &results, nil
}

type GetVolumesDetailParams struct {

	//
	// The unique identifier of the tenant or account.
	TenantId string `json:"-"`
}

type VolumeAttachment struct {
	Device   string `json:"device"`
	Id       string `json:"id"`
	ServerId string `json:"server_id"`
	VolumeId string `json:"volume_id"`
}

type Volume struct {
	Attachments      []VolumeAttachment `json:"attachments"`
	AvailabilityZone string             `json:"availability_zone"`
	Bootable         string             `json:"bootable"`
	CreatedAt        string             `json:"created_at"`
	Description      string             `json:"description"`
	ID               string             `json:"id"`
	Links            []struct {
		Href string `json:"href"`
		Rel  string `json:"rel"`
	} `json:"links"`
	Metadata                    map[string]string `json:"metadata"`
	Name                        string            `json:"name"`
	Os_Vol_Host_Attr_Host       string            `json:"os-vol-host-attr:host"`
	Os_Vol_Tenant_Attr_TenantID string            `json:"os-vol-tenant-attr:tenant_id"`
	Size                        int               `json:"size"`
	SnapshotID                  interface{}       `json:"snapshot_id"`
	SourceVolid                 interface{}       `json:"source_volid"`
	Status                      string            `json:"status"`
	VolumeType                  string            `json:"volume_type"`
}

type GetVolumesDetailResults struct {
	Volumes []Volume `json:"volumes"`
}

//
// Lists detailed information for all Block Storage volumes that the tenant who submits the request can access.
func getVolumesDetail(client *Client, args GetVolumesDetailParams) (*GetVolumesDetailResults, error) {

	argsAsJson, err := json.Marshal(args)
	if err != nil {
		return nil, err
	}

	urlPath := url.URL{Path: "volumes/detail"}
	url := client.endpoint.ResolveReference(&urlPath).String()

	var req *http.Request
	if string(argsAsJson) != "{}" {
		req, err = http.NewRequest("GET", url, bytes.NewBuffer(argsAsJson))
		if err != nil {
			return nil, err
		}
		req.Header.Set("Content-Type", "application/json")
	} else {
		req, err = http.NewRequest("GET", url, nil)
		if err != nil {
			return nil, err
		}
	}

	resp, err := client.handleRequest(req)
	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	switch resp.StatusCode {
	default:
		return nil, fmt.Errorf("invalid status (%d): %s", resp.StatusCode, body)
	case 200:
		break
	}

	var results GetVolumesDetailResults
	json.Unmarshal(body, &results)

	return &results, nil
}

type GetVolumeParams struct {

	//
	// The unique identifier of the tenant or account.
	TenantId string `json:"-"`

	// VolumeId is required.
	//
	// The unique identifier of an existing volume.
	VolumeId string `json:"-"`
}

type GetVolumeResults struct {
	Volume Volume `json:"volume"`
}

//
// Shows information about a specified volume.
// Preconditions
//
// The specified volume must exist. :
func getVolume(client *Client, args GetVolumeParams) (*GetVolumeResults, error) {

	argsAsJson, err := json.Marshal(args)
	if err != nil {
		return nil, err
	}

	urlPath := url.URL{Path: fmt.Sprintf("volumes/%s", args.VolumeId)}
	url := client.endpoint.ResolveReference(&urlPath).String()

	var req *http.Request
	if string(argsAsJson) != "{}" {
		req, err = http.NewRequest("GET", url, bytes.NewBuffer(argsAsJson))
		if err != nil {
			return nil, err
		}
		req.Header.Set("Content-Type", "application/json")
	} else {
		req, err = http.NewRequest("GET", url, nil)
		if err != nil {
			return nil, err
		}
	}

	resp, err := client.handleRequest(req)
	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	switch resp.StatusCode {
	default:
		return nil, fmt.Errorf("invalid status (%d): %s", resp.StatusCode, body)
	case 200:
		break
	}

	var results GetVolumeResults
	json.Unmarshal(body, &results)

	return &results, nil
}

type UpdateVolumeVolumeParams struct {
	Name string `json:"name,omitempty"`

	Description string `json:"description,omitempty"`
}

type UpdateVolumeParams struct {

	// Volume is required.

	Volume UpdateVolumeVolumeParams `json:"volume"`

	//
	// The unique identifier of the tenant or account.
	TenantId string `json:"-"`

	// VolumeId is required.
	//
	// The unique identifier of an existing volume.
	VolumeId string `json:"-"`
}

type UpdateVolumeResults struct {
	Volume Volume `json:"volume"`
}

//
// Updates a volume.
func updateVolume(client *Client, args UpdateVolumeParams) (*UpdateVolumeResults, error) {

	argsAsJson, err := json.Marshal(args)
	if err != nil {
		return nil, err
	}

	urlPath := url.URL{Path: fmt.Sprintf("volumes/%s", args.VolumeId)}
	url := client.endpoint.ResolveReference(&urlPath).String()

	var req *http.Request
	if string(argsAsJson) != "{}" {
		req, err = http.NewRequest("PUT", url, bytes.NewBuffer(argsAsJson))
		if err != nil {
			return nil, err
		}
		req.Header.Set("Content-Type", "application/json")
	} else {
		req, err = http.NewRequest("PUT", url, nil)
		if err != nil {
			return nil, err
		}
	}

	resp, err := client.handleRequest(req)
	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	switch resp.StatusCode {
	default:
		return nil, fmt.Errorf("invalid status (%d): %s", resp.StatusCode, body)
	case 200:
		break
	}

	var results UpdateVolumeResults
	json.Unmarshal(body, &results)

	return &results, nil
}

type DeleteVolumeParams struct {

	//
	// The unique identifier of the tenant or account.
	TenantId string `json:"-"`

	// VolumeId is required.
	//
	// The unique identifier of an existing volume.
	VolumeId string `json:"-"`
}

type DeleteVolumeResults struct {
}

//
// Deletes a specified volume.
// Preconditions
//
// Volume status must be available, in-use, error, or error_restoring.
// You cannot already have a snapshot related to the specified volume.
// You cannot delete a volume that is in a migration. :
// Asynchronous Postconditions
//
// The volume is deleted in volume index.
// The volume managed by OpenStack Block Storage is deleted in storage node. :
// Troubleshooting
//
// If volume status remains in deleting or becomes error_deleting the request failed. Ensure you meet the preconditions then investigate the storage backend.
// The volume managed by OpenStack Block Storage is not deleted from the storage system. :
func deleteVolume(client *Client, args DeleteVolumeParams) (*DeleteVolumeResults, error) {

	argsAsJson, err := json.Marshal(args)
	if err != nil {
		return nil, err
	}

	urlPath := url.URL{Path: fmt.Sprintf("volumes/%s", args.VolumeId)}
	url := client.endpoint.ResolveReference(&urlPath).String()

	var req *http.Request
	if string(argsAsJson) != "{}" {
		req, err = http.NewRequest("DELETE", url, bytes.NewBuffer(argsAsJson))
		if err != nil {
			return nil, err
		}
		req.Header.Set("Content-Type", "application/json")
	} else {
		req, err = http.NewRequest("DELETE", url, nil)
		if err != nil {
			return nil, err
		}
	}

	resp, err := client.handleRequest(req)
	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	switch resp.StatusCode {
	default:
		return nil, fmt.Errorf("invalid status (%d): %s", resp.StatusCode, body)
	case 202:
		break
	}

	var results DeleteVolumeResults
	json.Unmarshal(body, &results)

	return &results, nil
}

type CreateVolumeTypeVolumeTypeExtraSpecsParams struct {
	Capabilities string `json:"capabilities,omitempty"`
}

type CreateVolumeTypeVolumeTypeParams struct {

	//  The name of the volume type.
	Name string `json:"name,omitempty"`

	ExtraSpecs CreateVolumeTypeVolumeTypeExtraSpecsParams `json:"extra_specs,omitempty"`
}

type CreateVolumeTypeParams struct {

	// VolumeType is required.
	//  A partial representation of a volume type used in the creation process.
	VolumeType CreateVolumeTypeVolumeTypeParams `json:"volume_type"`

	//
	// The unique identifier of the tenant or account.
	TenantId string `json:"-"`
}

type CreateVolumeTypeResults struct {
	VolumeType VolumeType `json:"volume_type"`
}

//
// Creates a volume type.
func createVolumeType(client *Client, args CreateVolumeTypeParams) (*CreateVolumeTypeResults, error) {

	argsAsJson, err := json.Marshal(args)
	if err != nil {
		return nil, err
	}

	urlPath := url.URL{Path: "types"}
	url := client.endpoint.ResolveReference(&urlPath).String()

	var req *http.Request
	if string(argsAsJson) != "{}" {
		req, err = http.NewRequest("POST", url, bytes.NewBuffer(argsAsJson))
		if err != nil {
			return nil, err
		}
		req.Header.Set("Content-Type", "application/json")
	} else {
		req, err = http.NewRequest("POST", url, nil)
		if err != nil {
			return nil, err
		}
	}

	resp, err := client.handleRequest(req)
	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	switch resp.StatusCode {
	default:
		return nil, fmt.Errorf("invalid status (%d): %s", resp.StatusCode, body)
	case 200:
		break
	}

	var results CreateVolumeTypeResults
	json.Unmarshal(body, &results)

	return &results, nil
}

type CreateSnapshotSnapshotParams struct {

	//  [True/False] Indicate whether to snapshot, even if the volume is attached. Default==False.
	Force bool `json:"force,omitempty"`

	//  Name of the snapshot. The default is None.
	Name string `json:"name,omitempty"`

	//  Description of the snapshot. The default is None.
	Description string `json:"description,omitempty"`

	// VolumeId is required.
	//  To create a snapshot from an existing volume, specify the ID of the existing volume.
	VolumeId string `json:"volume_id"`
}

type CreateSnapshotParams struct {

	// Snapshot is required.
	//  A partial representation of a snapshot used in the creation process.
	Snapshot CreateSnapshotSnapshotParams `json:"snapshot"`

	//
	// The unique identifier of the tenant or account.
	TenantId string `json:"-"`
}

type CreateSnapshotResults struct {
	Snapshot Snapshot `json:"snapshot"`
}

//
// Creates a snapshot, which is a point-in-time complete copy of a volume. You can create a volume from the snapshot.
func createSnapshot(client *Client, args CreateSnapshotParams) (*CreateSnapshotResults, error) {

	argsAsJson, err := json.Marshal(args)
	if err != nil {
		return nil, err
	}

	urlPath := url.URL{Path: "snapshots"}
	url := client.endpoint.ResolveReference(&urlPath).String()

	var req *http.Request
	if string(argsAsJson) != "{}" {
		req, err = http.NewRequest("POST", url, bytes.NewBuffer(argsAsJson))
		if err != nil {
			return nil, err
		}
		req.Header.Set("Content-Type", "application/json")
	} else {
		req, err = http.NewRequest("POST", url, nil)
		if err != nil {
			return nil, err
		}
	}

	resp, err := client.handleRequest(req)
	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	switch resp.StatusCode {
	default:
		return nil, fmt.Errorf("invalid status (%d): %s", resp.StatusCode, body)
	case 200, 202:
		break
	}

	var results CreateSnapshotResults
	json.Unmarshal(body, &results)

	return &results, nil
}

type GetSnapshotsDetailParams struct {

	//
	// The unique identifier of the tenant or account.
	TenantId string `json:"-"`
}

type GetSnapshotsDetailResults struct {
	Snapshots []Snapshot `json:"snapshots"`
}

//
// Lists detailed information for all Block Storage snapshots that the tenant who submits the request can access.
func getSnapshotsDetail(client *Client, args GetSnapshotsDetailParams) (*GetSnapshotsDetailResults, error) {

	argsAsJson, err := json.Marshal(args)
	if err != nil {
		return nil, err
	}

	urlPath := url.URL{Path: "snapshots/detail"}
	url := client.endpoint.ResolveReference(&urlPath).String()

	var req *http.Request
	if string(argsAsJson) != "{}" {
		req, err = http.NewRequest("GET", url, bytes.NewBuffer(argsAsJson))
		if err != nil {
			return nil, err
		}
		req.Header.Set("Content-Type", "application/json")
	} else {
		req, err = http.NewRequest("GET", url, nil)
		if err != nil {
			return nil, err
		}
	}

	resp, err := client.handleRequest(req)
	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	switch resp.StatusCode {
	default:
		return nil, fmt.Errorf("invalid status (%d): %s", resp.StatusCode, body)
	case 200:
		break
	}

	var results GetSnapshotsDetailResults
	json.Unmarshal(body, &results)

	return &results, nil
}

type GetSnapshotParams struct {

	//
	// The unique identifier of the tenant or account.
	TenantId string `json:"-"`

	// SnapshotId is required.
	//
	// The unique identifier of an existing snapshot.
	SnapshotId string `json:"-"`
}

type GetSnapshotResults struct {
	Snapshot Snapshot `json:"snapshot"`
}

//
// Shows information for a specified snapshot.
func getSnapshot(client *Client, args GetSnapshotParams) (*GetSnapshotResults, error) {

	argsAsJson, err := json.Marshal(args)
	if err != nil {
		return nil, err
	}

	urlPath := url.URL{Path: fmt.Sprintf("snapshots/%s", args.SnapshotId)}
	url := client.endpoint.ResolveReference(&urlPath).String()

	var req *http.Request
	if string(argsAsJson) != "{}" {
		req, err = http.NewRequest("GET", url, bytes.NewBuffer(argsAsJson))
		if err != nil {
			return nil, err
		}
		req.Header.Set("Content-Type", "application/json")
	} else {
		req, err = http.NewRequest("GET", url, nil)
		if err != nil {
			return nil, err
		}
	}

	resp, err := client.handleRequest(req)
	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	switch resp.StatusCode {
	default:
		return nil, fmt.Errorf("invalid status (%d): %s", resp.StatusCode, body)
	case 200:
		break
	}

	var results GetSnapshotResults
	json.Unmarshal(body, &results)

	return &results, nil
}

type ListVersionsParams struct {
}

type Version struct {
	ID    string `json:"id"`
	Links []struct {
		Href string `json:"href"`
		Rel  string `json:"rel"`
	} `json:"links"`
	Media_Types []struct {
		Base string `json:"base"`
		Type string `json:"type"`
	} `json:"media-types"`
	Status  string `json:"status"`
	Updated string `json:"updated"`
}

type ListVersionsResults struct {
	Versions []Version `json:"versions"`
}

//
// Lists information about all Block Storage API versions.
func listVersions(client *Client, args ListVersionsParams) (*ListVersionsResults, error) {

	argsAsJson, err := json.Marshal(args)
	if err != nil {
		return nil, err
	}

	urlPath := url.URL{Path: "../.."}
	url := client.endpoint.ResolveReference(&urlPath).String()

	var req *http.Request
	if string(argsAsJson) != "{}" {
		req, err = http.NewRequest("GET", url, bytes.NewBuffer(argsAsJson))
		if err != nil {
			return nil, err
		}
		req.Header.Set("Content-Type", "application/json")
	} else {
		req, err = http.NewRequest("GET", url, nil)
		if err != nil {
			return nil, err
		}
	}

	resp, err := client.handleRequest(req)
	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	switch resp.StatusCode {
	default:
		return nil, fmt.Errorf("invalid status (%d): %s", resp.StatusCode, body)
	case 200, 300:
		break
	}

	var results ListVersionsResults
	json.Unmarshal(body, &results)

	return &results, nil
}

type VersionDetailsParams struct {
}

type VersionDetailsResults struct {
	Version Version `json:"version"`
}

//
// Shows details for Block Storage API v2.
func versionDetails(client *Client, args VersionDetailsParams) (*VersionDetailsResults, error) {

	argsAsJson, err := json.Marshal(args)
	if err != nil {
		return nil, err
	}

	urlPath := url.URL{Path: ".."}
	url := client.endpoint.ResolveReference(&urlPath).String()

	var req *http.Request
	if string(argsAsJson) != "{}" {
		req, err = http.NewRequest("GET", url, bytes.NewBuffer(argsAsJson))
		if err != nil {
			return nil, err
		}
		req.Header.Set("Content-Type", "application/json")
	} else {
		req, err = http.NewRequest("GET", url, nil)
		if err != nil {
			return nil, err
		}
	}

	resp, err := client.handleRequest(req)
	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	switch resp.StatusCode {
	default:
		return nil, fmt.Errorf("invalid status (%d): %s", resp.StatusCode, body)
	case 200, 203:
		break
	}

	var results VersionDetailsResults
	json.Unmarshal(body, &results)

	return &results, nil
}

type CreateVolumeVolumeParams struct {

	//  To create a volume from an existing volume, specify the ID of the existing volume. If specified, the volume is created with same size of the source volume.
	SourceVolid string `json:"source_volid,omitempty"`

	//  To create a volume from an existing snapshot, specify the ID of the existing volume snapshot. If specified, the volume is created in same availability zone and with same size of the snapshot.
	SnapshotId string `json:"snapshot_id,omitempty"`

	//  The ID of the image from which you want to create the volume. Required to create a bootable volume.
	ImageRef string `json:"imageRef,omitempty"`

	//  The associated volume type.
	VolumeType string `json:"volume_type,omitempty"`

	//  Enables or disables the bootable attribute. You can boot an instance from a bootable volume.
	Bootable bool `json:"bootable,omitempty"`

	//  One or more metadata key and value pairs to associate with the volume.
	Metadata interface{} `json:"metadata,omitempty"`

	//  The availability zone.
	AvailabilityZone string `json:"availability_zone,omitempty"`

	//  The volume description.
	Description string `json:"description,omitempty"`

	// Size is required.
	//  The size of the volume, in GBs.
	Size int `json:"size"`

	//  The volume name.
	Name string `json:"name,omitempty"`
}

type CreateVolumeParams struct {

	// Volume is required.

	Volume CreateVolumeVolumeParams `json:"volume"`

	//
	// The unique identifier of the tenant or account.
	TenantId string `json:"-"`
}

type CreateVolumeResults struct {
	Volume Volume `json:"volume"`
}

//
// Creates a volume.
// To create a bootable volume, include the image ID and set the bootable flag to true in the request body.
// Preconditions
//
// The user must have enough volume storage quota remaining to create a volume of size requested. :
// Asynchronous Postconditions
//
// With correct permissions, you can see the volume status as available through API calls.
// With correct access, you can see the created volume in the storage system that OpenStack Block Storage manages. :
// Troubleshooting
//
// If volume status remains creating or shows another error status, the request failed. Ensure you meet the preconditions then investigate the storage backend.
// Volume is not created in the storage system which OpenStack Block Storage manages.
// The storage node needs enough free storage space to match the specified size of the volume creation request. :
func createVolume(client *Client, args CreateVolumeParams) (*CreateVolumeResults, error) {

	argsAsJson, err := json.Marshal(args)
	if err != nil {
		return nil, err
	}

	urlPath := url.URL{Path: "volumes"}
	url := client.endpoint.ResolveReference(&urlPath).String()

	var req *http.Request
	if string(argsAsJson) != "{}" {
		req, err = http.NewRequest("POST", url, bytes.NewBuffer(argsAsJson))
		if err != nil {
			return nil, err
		}
		req.Header.Set("Content-Type", "application/json")
	} else {
		req, err = http.NewRequest("POST", url, nil)
		if err != nil {
			return nil, err
		}
	}

	resp, err := client.handleRequest(req)
	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	switch resp.StatusCode {
	default:
		return nil, fmt.Errorf("invalid status (%d): %s", resp.StatusCode, body)
	case 200, 202:
		break
	}

	var results CreateVolumeResults
	json.Unmarshal(body, &results)

	return &results, nil
}

type GetVolumesSimpleParams struct {

	//
	// The unique identifier of the tenant or account.
	TenantId string `json:"-"`
}

type GetVolumesSimpleResults struct {
	Volumes []Volume `json:"volumes"`
}

//
// Lists summary information for all Block Storage volumes that the tenant who submits the request can access.
func getVolumesSimple(client *Client, args GetVolumesSimpleParams) (*GetVolumesSimpleResults, error) {

	argsAsJson, err := json.Marshal(args)
	if err != nil {
		return nil, err
	}

	urlPath := url.URL{Path: "volumes"}
	url := client.endpoint.ResolveReference(&urlPath).String()

	var req *http.Request
	if string(argsAsJson) != "{}" {
		req, err = http.NewRequest("GET", url, bytes.NewBuffer(argsAsJson))
		if err != nil {
			return nil, err
		}
		req.Header.Set("Content-Type", "application/json")
	} else {
		req, err = http.NewRequest("GET", url, nil)
		if err != nil {
			return nil, err
		}
	}

	resp, err := client.handleRequest(req)
	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	switch resp.StatusCode {
	default:
		return nil, fmt.Errorf("invalid status (%d): %s", resp.StatusCode, body)
	case 200:
		break
	}

	var results GetVolumesSimpleResults
	json.Unmarshal(body, &results)

	return &results, nil
}

type GetVolumeTypeParams struct {

	//
	// The unique identifier of the tenant or account.
	TenantId string `json:"-"`

	// VolumeTypeId is required.
	//
	// The unique identifier for an existing volume type.
	VolumeTypeId string `json:"-"`
}

type GetVolumeTypeResults struct {
	VolumeType VolumeType `json:"volume_type"`
}

//
// Shows information about a specified volume type.
func getVolumeType(client *Client, args GetVolumeTypeParams) (*GetVolumeTypeResults, error) {

	argsAsJson, err := json.Marshal(args)
	if err != nil {
		return nil, err
	}

	urlPath := url.URL{Path: fmt.Sprintf("types/%s", args.VolumeTypeId)}
	url := client.endpoint.ResolveReference(&urlPath).String()

	var req *http.Request
	if string(argsAsJson) != "{}" {
		req, err = http.NewRequest("GET", url, bytes.NewBuffer(argsAsJson))
		if err != nil {
			return nil, err
		}
		req.Header.Set("Content-Type", "application/json")
	} else {
		req, err = http.NewRequest("GET", url, nil)
		if err != nil {
			return nil, err
		}
	}

	resp, err := client.handleRequest(req)
	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	switch resp.StatusCode {
	default:
		return nil, fmt.Errorf("invalid status (%d): %s", resp.StatusCode, body)
	case 200:
		break
	}

	var results GetVolumeTypeResults
	json.Unmarshal(body, &results)

	return &results, nil
}

type DeleteSnapshotParams struct {

	//
	// The unique identifier of the tenant or account.
	TenantId string `json:"-"`

	// SnapshotId is required.
	//
	// The unique identifier of an existing snapshot.
	SnapshotId string `json:"-"`
}

type DeleteSnapshotResults struct {
}

//
// Deletes a specified snapshot.
func deleteSnapshot(client *Client, args DeleteSnapshotParams) (*DeleteSnapshotResults, error) {

	argsAsJson, err := json.Marshal(args)
	if err != nil {
		return nil, err
	}

	urlPath := url.URL{Path: fmt.Sprintf("snapshots/%s", args.SnapshotId)}
	url := client.endpoint.ResolveReference(&urlPath).String()

	var req *http.Request
	if string(argsAsJson) != "{}" {
		req, err = http.NewRequest("DELETE", url, bytes.NewBuffer(argsAsJson))
		if err != nil {
			return nil, err
		}
		req.Header.Set("Content-Type", "application/json")
	} else {
		req, err = http.NewRequest("DELETE", url, nil)
		if err != nil {
			return nil, err
		}
	}

	resp, err := client.handleRequest(req)
	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	switch resp.StatusCode {
	default:
		return nil, fmt.Errorf("invalid status (%d): %s", resp.StatusCode, body)
	case 202:
		break
	}

	var results DeleteSnapshotResults
	json.Unmarshal(body, &results)

	return &results, nil
}

type ListExtensionsCinderV2Params struct {
}

type Extension struct {
	Alias       string        `json:"alias"`
	Description string        `json:"description"`
	Links       []interface{} `json:"links"`
	Name        string        `json:"name"`
	Namespace   string        `json:"namespace"`
	Updated     string        `json:"updated"`
}

type ListExtensionsCinderV2Results struct {
	Extensions []Extension `json:"extensions"`
}

//
// Lists Block Storage API extensions.
func listExtensionsCinderV2(client *Client, args ListExtensionsCinderV2Params) (*ListExtensionsCinderV2Results, error) {

	argsAsJson, err := json.Marshal(args)
	if err != nil {
		return nil, err
	}

	urlPath := url.URL{Path: ".."}
	url := client.endpoint.ResolveReference(&urlPath).String()

	var req *http.Request
	if string(argsAsJson) != "{}" {
		req, err = http.NewRequest("GET", url, bytes.NewBuffer(argsAsJson))
		if err != nil {
			return nil, err
		}
		req.Header.Set("Content-Type", "application/json")
	} else {
		req, err = http.NewRequest("GET", url, nil)
		if err != nil {
			return nil, err
		}
	}

	resp, err := client.handleRequest(req)
	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	switch resp.StatusCode {
	default:
		return nil, fmt.Errorf("invalid status (%d): %s", resp.StatusCode, body)
	case 200, 300:
		break
	}

	var results ListExtensionsCinderV2Results
	json.Unmarshal(body, &results)

	return &results, nil
}

type GetVolumeTypesParams struct {

	//
	// The unique identifier of the tenant or account.
	TenantId string `json:"-"`
}

type GetVolumeTypesResults struct {
	VolumeTypes []VolumeType `json:"volume_types"`
}

//
// Lists volume types.
func getVolumeTypes(client *Client, args GetVolumeTypesParams) (*GetVolumeTypesResults, error) {

	argsAsJson, err := json.Marshal(args)
	if err != nil {
		return nil, err
	}

	urlPath := url.URL{Path: "types"}
	url := client.endpoint.ResolveReference(&urlPath).String()

	var req *http.Request
	if string(argsAsJson) != "{}" {
		req, err = http.NewRequest("GET", url, bytes.NewBuffer(argsAsJson))
		if err != nil {
			return nil, err
		}
		req.Header.Set("Content-Type", "application/json")
	} else {
		req, err = http.NewRequest("GET", url, nil)
		if err != nil {
			return nil, err
		}
	}

	resp, err := client.handleRequest(req)
	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	switch resp.StatusCode {
	default:
		return nil, fmt.Errorf("invalid status (%d): %s", resp.StatusCode, body)
	case 200:
		break
	}

	var results GetVolumeTypesResults
	json.Unmarshal(body, &results)

	return &results, nil
}

type DeleteVolumeTypeParams struct {

	//
	// The unique identifier of the tenant or account.
	TenantId string `json:"-"`

	// VolumeTypeId is required.
	//
	// The unique identifier for an existing volume type.
	VolumeTypeId string `json:"-"`
}

type DeleteVolumeTypeResults struct {
}

//
// Deletes a specified volume type.
func deleteVolumeType(client *Client, args DeleteVolumeTypeParams) (*DeleteVolumeTypeResults, error) {

	argsAsJson, err := json.Marshal(args)
	if err != nil {
		return nil, err
	}

	urlPath := url.URL{Path: fmt.Sprintf("types/%s", args.VolumeTypeId)}
	url := client.endpoint.ResolveReference(&urlPath).String()

	var req *http.Request
	if string(argsAsJson) != "{}" {
		req, err = http.NewRequest("DELETE", url, bytes.NewBuffer(argsAsJson))
		if err != nil {
			return nil, err
		}
		req.Header.Set("Content-Type", "application/json")
	} else {
		req, err = http.NewRequest("DELETE", url, nil)
		if err != nil {
			return nil, err
		}
	}

	resp, err := client.handleRequest(req)
	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	switch resp.StatusCode {
	default:
		return nil, fmt.Errorf("invalid status (%d): %s", resp.StatusCode, body)
	case 202:
		break
	}

	var results DeleteVolumeTypeResults
	json.Unmarshal(body, &results)

	return &results, nil
}

type UpdateSnapshotMetadataMetadataParams struct {

	// Key is required.

	Key string `json:"key"`
}

type UpdateSnapshotMetadataParams struct {

	// Metadata is required.
	//  One or more metadata key and value pairs to set or unset for the snapshot. To unset a metadata key value, specify only the key name. To set a metadata key value, specify the key and value pair. The Block Storage server does not respect case-sensitive key names. For example, if you specify both "key": "v1" and "KEY": "V1", the server sets and returns only the KEY key and value pair.
	Metadata UpdateSnapshotMetadataMetadataParams `json:"metadata"`

	//
	// The unique identifier of the tenant or account.
	TenantId string `json:"-"`

	// SnapshotId is required.
	//
	// The unique identifier of an existing snapshot.
	SnapshotId string `json:"-"`
}

type UpdateSnapshotMetadataResults struct {
	Metadata struct {
		Key string `json:"key"`
	} `json:"metadata"`
}

//
// Updates the metadata for a specified snapshot.
func updateSnapshotMetadata(client *Client, args UpdateSnapshotMetadataParams) (*UpdateSnapshotMetadataResults, error) {

	argsAsJson, err := json.Marshal(args)
	if err != nil {
		return nil, err
	}

	urlPath := url.URL{Path: fmt.Sprintf("snapshots/%s/metadata", args.SnapshotId)}
	url := client.endpoint.ResolveReference(&urlPath).String()

	var req *http.Request
	if string(argsAsJson) != "{}" {
		req, err = http.NewRequest("PUT", url, bytes.NewBuffer(argsAsJson))
		if err != nil {
			return nil, err
		}
		req.Header.Set("Content-Type", "application/json")
	} else {
		req, err = http.NewRequest("PUT", url, nil)
		if err != nil {
			return nil, err
		}
	}

	resp, err := client.handleRequest(req)
	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	switch resp.StatusCode {
	default:
		return nil, fmt.Errorf("invalid status (%d): %s", resp.StatusCode, body)
	case 200:
		break
	}

	var results UpdateSnapshotMetadataResults
	json.Unmarshal(body, &results)

	return &results, nil
}
