stacks
diff --git a/openstack/client.go b/openstack/client.go
index 9c12dca..5fce3d6 100644
--- a/openstack/client.go
+++ b/openstack/client.go
@@ -214,3 +214,13 @@
 	}
 	return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
 }
+
+// NewOrchestrationV1 creates a ServiceClient that may be used to access the v1 orchestration service.
+func NewOrchestrationV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
+	eo.ApplyDefaults("orchestration")
+	url, err := client.EndpointLocator(eo)
+	if err != nil {
+		return nil, err
+	}
+	return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
+}
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
+}
diff --git a/openstack/orchestration/v1/stacks/results.go b/openstack/orchestration/v1/stacks/results.go
new file mode 100644
index 0000000..9e11f82
--- /dev/null
+++ b/openstack/orchestration/v1/stacks/results.go
@@ -0,0 +1,219 @@
+package stacks
+
+import (
+	"time"
+
+	"github.com/mitchellh/mapstructure"
+	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/gophercloud/pagination"
+)
+
+type CreateStack struct {
+	ID    string             `mapstructure:"id"`
+	Links []gophercloud.Link `mapstructure:"links"`
+}
+
+type CreateResult struct {
+	gophercloud.Result
+}
+
+func (r CreateResult) Extract() (*CreateStack, error) {
+	if r.Err != nil {
+		return nil, r.Err
+	}
+
+	var res struct {
+		Stack *CreateStack `json:"stack"`
+	}
+
+	if err := mapstructure.Decode(r.Body, &res); err != nil {
+		return nil, err
+	}
+
+	return res.Stack, nil
+}
+
+type AdoptResult struct {
+	gophercloud.Result
+}
+
+// StackPage is a pagination.Pager that is returned from a call to the List function.
+type StackPage struct {
+	pagination.SinglePageBase
+}
+
+// IsEmpty returns true if a ListResult contains no Stacks.
+func (r StackPage) IsEmpty() (bool, error) {
+	stacks, err := ExtractStacks(r)
+	if err != nil {
+		return true, err
+	}
+	return len(stacks) == 0, nil
+}
+
+type ListStack struct {
+	CreationTime time.Time          `mapstructure:"-"`
+	Description  string             `mapstructure:"description"`
+	ID           string             `mapstructure:"id"`
+	Links        []gophercloud.Link `mapstructure:"links"`
+	Name         string             `mapstructure:"stack_name"`
+	Status       string             `mapstructure:"stack_status"`
+	StausReason  string             `mapstructure:"stack_status_reason"`
+	UpdatedTime  time.Time          `mapstructure:"-"`
+}
+
+// ExtractStacks extracts and returns a slice of Stacks. It is used while iterating
+// over a stacks.List call.
+func ExtractStacks(page pagination.Page) ([]ListStack, error) {
+	var res struct {
+		Stacks []ListStack `json:"stacks"`
+	}
+
+	err := mapstructure.Decode(page.(StackPage).Body, &res)
+	return res.Stacks, err
+}
+
+type GetStack struct {
+	Capabilities        []interface{}       `mapstructure:"capabilities"`
+	CreationTime        time.Time           `mapstructure:"-"`
+	Description         string              `mapstructure:"description"`
+	DisableRollback     bool                `mapstructure:"disable_rollback"`
+	ID                  string              `mapstructure:"id"`
+	Links               []gophercloud.Link  `mapstructure:"links"`
+	NotificationTopics  []interface{}       `mapstructure:"notification_topics"`
+	Outputs             []map[string]string `mapstructure:"outputs"`
+	Parameters          map[string]string   `mapstructure:"parameters"`
+	Name                string              `mapstructure:"stack_name"`
+	Status              string              `mapstructure:"stack_status"`
+	StausReason         string              `mapstructure:"stack_status_reason"`
+	TemplateDescription string              `mapstructure:"template_description"`
+	Timeout             int                 `mapstructure:"timeout_mins"`
+	UpdatedTime         time.Time           `mapstructure:"-"`
+}
+
+type GetResult struct {
+	gophercloud.Result
+}
+
+func (r GetResult) Extract() (*GetStack, error) {
+	if r.Err != nil {
+		return nil, r.Err
+	}
+
+	var res struct {
+		Stack *GetStack `json:"stack"`
+	}
+
+	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{})["stack"].(map[string]interface{})
+
+	if date, ok := b["creation_time"]; ok && date != nil {
+		t, err := time.Parse(time.RFC3339, date.(string))
+		if err != nil {
+			return nil, err
+		}
+		res.Stack.CreationTime = t
+	}
+
+	if date, ok := b["updated_time"]; ok && date != nil {
+		t, err := time.Parse(time.RFC3339, date.(string))
+		if err != nil {
+			return nil, err
+		}
+		res.Stack.UpdatedTime = t
+	}
+
+	return res.Stack, err
+}
+
+type UpdateResult struct {
+	gophercloud.ErrResult
+}
+
+type DeleteResult struct {
+	gophercloud.ErrResult
+}
+
+type PreviewStack struct {
+	Capabilities        []interface{}       `mapstructure:"capabilities"`
+	CreationTime        time.Time           `mapstructure:"-"`
+	Description         string              `mapstructure:"description"`
+	DisableRollback     bool                `mapstructure:"disable_rollback"`
+	ID                  string              `mapstructure:"id"`
+	Links               []gophercloud.Link  `mapstructure:"links"`
+	Name                string              `mapstructure:"stack_name"`
+	NotificationTopics  []interface{}       `mapstructure:"notification_topics"`
+	Parameters          map[string]string   `mapstructure:"parameters"`
+	Resources           []map[string]string `mapstructure:"resources"`
+	Status              string              `mapstructure:"stack_status"`
+	StausReason         string              `mapstructure:"stack_status_reason"`
+	TemplateDescription string              `mapstructure:"template_description"`
+	Timeout             int                 `mapstructure:"timeout_mins"`
+	UpdatedTime         time.Time           `mapstructure:"-"`
+}
+
+type PreviewResult struct {
+	gophercloud.Result
+}
+
+func (r PreviewResult) Extract() (*PreviewStack, error) {
+	if r.Err != nil {
+		return nil, r.Err
+	}
+
+	var res struct {
+		Stack *PreviewStack `json:"stack"`
+	}
+
+	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{})["stack"].(map[string]interface{})
+
+	if date, ok := b["creation_time"]; ok && date != nil {
+		t, err := time.Parse(time.RFC3339, date.(string))
+		if err != nil {
+			return nil, err
+		}
+		res.Stack.CreationTime = t
+	}
+
+	if date, ok := b["updated_time"]; ok && date != nil {
+		t, err := time.Parse(time.RFC3339, date.(string))
+		if err != nil {
+			return nil, err
+		}
+		res.Stack.UpdatedTime = t
+	}
+
+	return res.Stack, err
+}
+
+type AbandonStack struct {
+}
+
+type AbandonResult struct {
+	gophercloud.Result
+}
diff --git a/openstack/orchestration/v1/stacks/urls.go b/openstack/orchestration/v1/stacks/urls.go
new file mode 100644
index 0000000..385af48
--- /dev/null
+++ b/openstack/orchestration/v1/stacks/urls.go
@@ -0,0 +1,55 @@
+package stacks
+
+import "github.com/rackspace/gophercloud"
+
+func createURL(c *gophercloud.ServiceClient) string {
+	return c.ServiceURL("stacks")
+}
+
+func adoptURL(c *gophercloud.ServiceClient) string {
+	return createURL(c)
+}
+
+func listURL(c *gophercloud.ServiceClient) string {
+	return createURL(c)
+}
+
+func getURL(c *gophercloud.ServiceClient, name, id string) string {
+	return c.ServiceURL("stacks", name, id)
+}
+
+func updateURL(c *gophercloud.ServiceClient, name, id string) string {
+	return getURL(c, name, id)
+}
+
+func deleteURL(c *gophercloud.ServiceClient, name, id string) string {
+	return getURL(c, name, id)
+}
+
+func previewURL(c *gophercloud.ServiceClient) string {
+	return c.ServiceURL("stacks", "preview")
+}
+
+func abandonURL(c *gophercloud.ServiceClient, name, id string) string {
+	return c.ServiceURL("stacks", name, id, "abandon")
+}
+
+func createSnapshotURL(c *gophercloud.ServiceClient, stackName, stackID string) string {
+	return c.ServiceURL("stacks", stackName, stackID, "snapshots")
+}
+
+func listSnapshotsURL(c *gophercloud.ServiceClient, stackName, stackID string) string {
+	return createSnapshotURL(c, stackName, stackID)
+}
+
+func getSnapshotURL(c *gophercloud.ServiceClient, stackName, stackID, snapshotID string) string {
+	return c.ServiceURL("stacks", stackName, stackID, "snapshots", snapshotID)
+}
+
+func restoreSnapshotURL(c *gophercloud.ServiceClient, stackName, stackID, snapshotID string) string {
+	return getSnapshotURL(c, stackName, stackID, snapshotID)
+}
+
+func deleteSnapshotURL(c *gophercloud.ServiceClient, stackName, stackID, snapshotID string) string {
+	return getSnapshotURL(c, stackName, stackID, snapshotID)
+}