stacks
diff --git a/openstack/orchestration/v1/stacks/requests.go b/openstack/orchestration/v1/stacks/requests.go
new file mode 100644
index 0000000..50b900d
--- /dev/null
+++ b/openstack/orchestration/v1/stacks/requests.go
@@ -0,0 +1,429 @@
+package stacks
+
+import (
+	"errors"
+
+	"github.com/racker/perigee"
+	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/gophercloud/pagination"
+)
+
+// CreateOptsBuilder is the interface options structs have to satisfy in order
+// to be used in the main Create operation in this package. Since many
+// extensions decorate or modify the common logic, it is useful for them to
+// satisfy a basic interface in order for them to be used.
+type CreateOptsBuilder interface {
+	ToStackCreateMap() (map[string]interface{}, error)
+}
+
+// CreateOpts is the common options struct used in this package's Create
+// operation.
+type CreateOpts struct {
+	DisableRollback *bool
+	Environment     string
+	Files           map[string]interface{}
+	Name            string
+	Parameters      map[string]string
+	Template        string
+	TemplateURL     string
+	Timeout         int
+}
+
+// ToStackCreateMap casts a CreateOpts struct to a map.
+func (opts CreateOpts) ToStackCreateMap() (map[string]interface{}, error) {
+	s := make(map[string]interface{})
+
+	if opts.Name == "" {
+		return s, errors.New("Required field 'Name' not provided.")
+	}
+	s["stack_name"] = opts.Name
+
+	if opts.Template != "" {
+		s["template"] = opts.Template
+	} else if opts.TemplateURL != "" {
+		s["template_url"] = opts.TemplateURL
+	} else {
+		return s, errors.New("Either Template or TemplateURL must be provided.")
+	}
+
+	if opts.DisableRollback != nil {
+		s["disable_rollback"] = &opts.DisableRollback
+	}
+
+	if opts.Environment != "" {
+		s["environment"] = opts.Environment
+	}
+	if opts.Files != nil {
+		s["files"] = opts.Files
+	}
+	if opts.Parameters != nil {
+		s["parameters"] = opts.Parameters
+	}
+
+	if opts.Timeout != 0 {
+		s["timeout_mins"] = opts.Timeout
+	}
+
+	return s, nil
+}
+
+// Create accepts a CreateOpts struct and creates a new stack using the values
+// provided.
+func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
+	var res CreateResult
+
+	reqBody, err := opts.ToStackCreateMap()
+	if err != nil {
+		res.Err = err
+		return res
+	}
+
+	// Send request to API
+	_, res.Err = perigee.Request("POST", createURL(c), perigee.Options{
+		MoreHeaders: c.AuthenticatedHeaders(),
+		ReqBody:     &reqBody,
+		Results:     &res.Body,
+		OkCodes:     []int{201},
+	})
+	return res
+}
+
+// AdoptOptsBuilder is the interface options structs have to satisfy in order
+// to be used in the Adopt function in this package. Since many
+// extensions decorate or modify the common logic, it is useful for them to
+// satisfy a basic interface in order for them to be used.
+type AdoptOptsBuilder interface {
+	ToStackAdoptMap() (map[string]interface{}, error)
+}
+
+// AdoptOpts is the common options struct used in this package's Adopt
+// operation.
+type AdoptOpts struct {
+	AdoptStackData  string
+	DisableRollback *bool
+	Environment     string
+	Files           map[string]interface{}
+	Name            string
+	Parameters      map[string]string
+	Template        string
+	TemplateURL     string
+	Timeout         int
+}
+
+// ToStackAdoptMap casts a CreateOpts struct to a map.
+func (opts AdoptOpts) ToStackAdoptMap() (map[string]interface{}, error) {
+	s := make(map[string]interface{})
+
+	if opts.Name == "" {
+		return s, errors.New("Required field 'Name' not provided.")
+	}
+	s["stack_name"] = opts.Name
+
+	if opts.Template != "" {
+		s["template"] = opts.Template
+	} else if opts.TemplateURL != "" {
+		s["template_url"] = opts.TemplateURL
+	} else {
+		return s, errors.New("Either Template or TemplateURL must be provided.")
+	}
+
+	if opts.AdoptStackData == "" {
+		return s, errors.New("Required field 'AdoptStackData' not provided.")
+	}
+	s["adopt_stack_data"] = opts.AdoptStackData
+
+	if opts.DisableRollback != nil {
+		s["disable_rollback"] = &opts.DisableRollback
+	}
+
+	if opts.Environment != "" {
+		s["environment"] = opts.Environment
+	}
+	if opts.Files != nil {
+		s["files"] = opts.Files
+	}
+	if opts.Parameters != nil {
+		s["parameters"] = opts.Parameters
+	}
+
+	if opts.Timeout != 0 {
+		s["timeout_mins"] = opts.Timeout
+	}
+
+	return map[string]interface{}{"stack": s}, nil
+}
+
+// Adopt accepts an AdoptOpts struct and creates a new stack using the resources
+// from another stack.
+func Adopt(c *gophercloud.ServiceClient, opts AdoptOptsBuilder) CreateResult {
+	var res CreateResult
+
+	reqBody, err := opts.ToStackAdoptMap()
+	if err != nil {
+		res.Err = err
+		return res
+	}
+
+	// Send request to API
+	_, res.Err = perigee.Request("POST", adoptURL(c), perigee.Options{
+		MoreHeaders: c.AuthenticatedHeaders(),
+		ReqBody:     &reqBody,
+		Results:     &res.Body,
+		OkCodes:     []int{201},
+	})
+	return res
+}
+
+// SortDir is a type for specifying in which direction to sort a list of stacks.
+type SortDir string
+
+// SortKey is a type for specifying by which key to sort a list of stacks.
+type SortKey string
+
+var (
+	// SortAsc is used to sort a list of stacks in ascending order.
+	SortAsc SortDir = "asc"
+	// SortDesc is used to sort a list of stacks in descending order.
+	SortDesc SortDir = "desc"
+	// SortName is used to sort a list of stacks by name.
+	SortName SortKey = "name"
+	// SortStatus is used to sort a list of stacks by status.
+	SortStatus SortKey = "status"
+	// SortCreatedAt is used to sort a list of stacks by date created.
+	SortCreatedAt SortKey = "created_at"
+	// SortUpdatedAt is used to sort a list of stacks by date updated.
+	SortUpdatedAt SortKey = "updated_at"
+)
+
+// ListOptsBuilder allows extensions to add additional parameters to the
+// List request.
+type ListOptsBuilder interface {
+	ToStackListQuery() (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 network attributes you want to see returned. SortKey allows you to sort
+// by a particular network attribute. SortDir sets the direction, and is either
+// `asc' or `desc'. Marker and Limit are used for pagination.
+type ListOpts struct {
+	Status  string  `q:"status"`
+	Name    string  `q:"name"`
+	Marker  string  `q:"marker"`
+	Limit   int     `q:"limit"`
+	SortKey SortKey `q:"sort_keys"`
+	SortDir SortDir `q:"sort_dir"`
+}
+
+// ToStackListQuery formats a ListOpts into a query string.
+func (opts ListOpts) ToStackListQuery() (string, error) {
+	q, err := gophercloud.BuildQueryString(opts)
+	if err != nil {
+		return "", err
+	}
+	return q.String(), nil
+}
+
+// List returns a Pager which allows you to iterate over a collection of
+// stacks. It accepts a ListOpts struct, which allows you to filter and sort
+// the returned collection for greater efficiency.
+func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
+	url := listURL(c)
+	if opts != nil {
+		query, err := opts.ToStackListQuery()
+		if err != nil {
+			return pagination.Pager{Err: err}
+		}
+		url += query
+	}
+
+	createPage := func(r pagination.PageResult) pagination.Page {
+		return StackPage{pagination.SinglePageBase(r)}
+	}
+	return pagination.NewPager(c, url, createPage)
+}
+
+// Get retreives a stack based on the stack name and stack ID.
+func Get(c *gophercloud.ServiceClient, stackName, stackID string) GetResult {
+	var res GetResult
+
+	// Send request to API
+	_, res.Err = perigee.Request("GET", getURL(c, stackName, stackID), perigee.Options{
+		MoreHeaders: c.AuthenticatedHeaders(),
+		Results:     &res.Body,
+		OkCodes:     []int{200},
+	})
+	return res
+}
+
+// UpdateOptsBuilder is the interface options structs have to satisfy in order
+// to be used in the Update operation in this package.
+type UpdateOptsBuilder interface {
+	ToStackUpdateMap() (map[string]interface{}, error)
+}
+
+// UpdateOpts contains the common options struct used in this package's Update
+// operation.
+type UpdateOpts struct {
+	Environment string
+	Files       map[string]interface{}
+	Parameters  map[string]string
+	Template    string
+	TemplateURL string
+	Timeout     int
+}
+
+// ToStackUpdateMap casts a CreateOpts struct to a map.
+func (opts UpdateOpts) ToStackUpdateMap() (map[string]interface{}, error) {
+	s := make(map[string]interface{})
+
+	if opts.Template != "" {
+		s["template"] = opts.Template
+	} else if opts.TemplateURL != "" {
+		s["template_url"] = opts.TemplateURL
+	} else {
+		return s, errors.New("Either Template or TemplateURL must be provided.")
+	}
+
+	if opts.Environment != "" {
+		s["environment"] = opts.Environment
+	}
+
+	if opts.Files != nil {
+		s["files"] = opts.Files
+	}
+
+	if opts.Parameters != nil {
+		s["parameters"] = opts.Parameters
+	}
+
+	if opts.Timeout != 0 {
+		s["timeout_mins"] = opts.Timeout
+	}
+
+	return s, nil
+}
+
+// Update accepts an UpdateOpts struct and updates an existing stack using the values
+// provided.
+func Update(c *gophercloud.ServiceClient, stackName, stackID string, opts UpdateOptsBuilder) UpdateResult {
+	var res UpdateResult
+
+	reqBody, err := opts.ToStackUpdateMap()
+	if err != nil {
+		res.Err = err
+		return res
+	}
+
+	// Send request to API
+	_, res.Err = perigee.Request("PUT", updateURL(c, stackName, stackID), perigee.Options{
+		MoreHeaders: c.AuthenticatedHeaders(),
+		ReqBody:     &reqBody,
+		OkCodes:     []int{202},
+	})
+	return res
+}
+
+// Delete deletes a stack based on the stack name and stack ID.
+func Delete(c *gophercloud.ServiceClient, stackName, stackID string) DeleteResult {
+	var res DeleteResult
+
+	// Send request to API
+	_, res.Err = perigee.Request("DELETE", deleteURL(c, stackName, stackID), perigee.Options{
+		MoreHeaders: c.AuthenticatedHeaders(),
+		OkCodes:     []int{204},
+	})
+	return res
+}
+
+// PreviewOptsBuilder is the interface options structs have to satisfy in order
+// to be used in the Preview operation in this package.
+type PreviewOptsBuilder interface {
+	ToStackPreviewMap() (map[string]interface{}, error)
+}
+
+// PreviewOpts contains the common options struct used in this package's Preview
+// operation.
+type PreviewOpts struct {
+	DisableRollback *bool
+	Environment     string
+	Files           map[string]interface{}
+	Name            string
+	Parameters      map[string]string
+	Template        string
+	TemplateURL     string
+	Timeout         int
+}
+
+// ToStackPreviewMap casts a PreviewOpts struct to a map.
+func (opts PreviewOpts) ToStackPreviewMap() (map[string]interface{}, error) {
+	s := make(map[string]interface{})
+
+	if opts.Name == "" {
+		return s, errors.New("Required field 'Name' not provided.")
+	}
+	s["stack_name"] = opts.Name
+
+	if opts.Template != "" {
+		s["template"] = opts.Template
+	} else if opts.TemplateURL != "" {
+		s["template_url"] = opts.TemplateURL
+	} else {
+		return s, errors.New("Either Template or TemplateURL must be provided.")
+	}
+
+	if opts.DisableRollback != nil {
+		s["disable_rollback"] = &opts.DisableRollback
+	}
+
+	if opts.Environment != "" {
+		s["environment"] = opts.Environment
+	}
+	if opts.Files != nil {
+		s["files"] = opts.Files
+	}
+	if opts.Parameters != nil {
+		s["parameters"] = opts.Parameters
+	}
+
+	if opts.Timeout != 0 {
+		s["timeout_mins"] = opts.Timeout
+	}
+
+	return s, nil
+}
+
+// Preview accepts a PreviewOptsBuilder interface and creates a preview of a stack using the values
+// provided.
+func Preview(c *gophercloud.ServiceClient, opts PreviewOptsBuilder) PreviewResult {
+	var res PreviewResult
+
+	reqBody, err := opts.ToStackPreviewMap()
+	if err != nil {
+		res.Err = err
+		return res
+	}
+
+	// Send request to API
+	_, res.Err = perigee.Request("POST", previewURL(c), perigee.Options{
+		MoreHeaders: c.AuthenticatedHeaders(),
+		ReqBody:     &reqBody,
+		Results:     &res.Body,
+		OkCodes:     []int{200},
+	})
+	return res
+}
+
+// Abandon deletes the stack with the provided stackName and stackID, but leaves its
+// resources intact, and returns data describing the stack and its resources.
+func Abandon(c *gophercloud.ServiceClient, stackName, stackID string) AbandonResult {
+	var res AbandonResult
+
+	// Send request to API
+	_, res.Err = perigee.Request("POST", previewURL(c), perigee.Options{
+		MoreHeaders: c.AuthenticatedHeaders(),
+		Results:     &res.Body,
+		OkCodes:     []int{200},
+	})
+	return res
+}