diff --git a/builder/openstack/builder.go b/builder/openstack/builder.go index ab60afc0e..2256ad80e 100644 --- a/builder/openstack/builder.go +++ b/builder/openstack/builder.go @@ -67,6 +67,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe // Build the steps steps := []multistep.Step{ + &StepLoadExtensions{}, &StepLoadFlavor{ Flavor: b.config.Flavor, }, @@ -94,6 +95,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe SSHWaitTimeout: b.config.SSHTimeout(), }, &common.StepProvision{}, + &StepStopServer{}, &stepCreateImage{}, } diff --git a/builder/openstack/step_load_extensions.go b/builder/openstack/step_load_extensions.go new file mode 100644 index 000000000..095863612 --- /dev/null +++ b/builder/openstack/step_load_extensions.go @@ -0,0 +1,58 @@ +package openstack + +import ( + "fmt" + "log" + + "github.com/mitchellh/multistep" + "github.com/mitchellh/packer/packer" + "github.com/rackspace/gophercloud/openstack/compute/v2/extensions" + "github.com/rackspace/gophercloud/pagination" +) + +// StepLoadExtensions gets the FlavorRef from a Flavor. It first assumes +// that the Flavor is a ref and verifies it. Otherwise, it tries to find +// the flavor by name. +type StepLoadExtensions struct{} + +func (s *StepLoadExtensions) Run(state multistep.StateBag) multistep.StepAction { + config := state.Get("config").(Config) + ui := state.Get("ui").(packer.Ui) + + // We need the v2 compute client + client, err := config.computeV2Client() + if err != nil { + err = fmt.Errorf("Error initializing compute client: %s", err) + state.Put("error", err) + return multistep.ActionHalt + } + + ui.Say("Discovering enabled extensions...") + result := make(map[string]struct{}, 15) + pager := extensions.List(client) + err = pager.EachPage(func(p pagination.Page) (bool, error) { + // Extract the extensions from this page + exts, err := extensions.ExtractExtensions(p) + if err != nil { + return false, err + } + + for _, ext := range exts { + log.Printf("[DEBUG] Discovered extension: %s", ext.Alias) + result[ext.Alias] = struct{}{} + } + + return true, nil + }) + if err != nil { + err = fmt.Errorf("Error loading extensions: %s", err) + state.Put("error", err) + return multistep.ActionHalt + } + + state.Put("extensions", result) + return multistep.ActionContinue +} + +func (s *StepLoadExtensions) Cleanup(state multistep.StateBag) { +} diff --git a/builder/openstack/step_stop_server.go b/builder/openstack/step_stop_server.go new file mode 100644 index 000000000..9b83fd89b --- /dev/null +++ b/builder/openstack/step_stop_server.go @@ -0,0 +1,58 @@ +package openstack + +import ( + "fmt" + + "github.com/mitchellh/multistep" + "github.com/mitchellh/packer/packer" + "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/startstop" + "github.com/rackspace/gophercloud/openstack/compute/v2/servers" +) + +type StepStopServer struct{} + +func (s *StepStopServer) Run(state multistep.StateBag) multistep.StepAction { + ui := state.Get("ui").(packer.Ui) + config := state.Get("config").(Config) + extensions := state.Get("extensions").(map[string]struct{}) + server := state.Get("server").(*servers.Server) + + // Verify we have the extension + if _, ok := extensions["os-server-start-stop"]; !ok { + ui.Say("OpenStack cluster doesn't support stop, skipping...") + return multistep.ActionContinue + } + + // We need the v2 compute client + client, err := config.computeV2Client() + if err != nil { + err = fmt.Errorf("Error initializing compute client: %s", err) + state.Put("error", err) + return multistep.ActionHalt + } + + ui.Say("Stopping server...") + if err := startstop.Stop(client, server.ID).ExtractErr(); err != nil { + err = fmt.Errorf("Error stopping server: %s", err) + state.Put("error", err) + return multistep.ActionHalt + } + + ui.Message("Waiting for server to stop...") + stateChange := StateChangeConf{ + Pending: []string{"ACTIVE"}, + Target: "STOPPED", + Refresh: ServerStateRefreshFunc(client, server), + StepState: state, + } + if _, err := WaitForState(&stateChange); err != nil { + err := fmt.Errorf("Error waiting for server (%s) to stop: %s", server.ID, err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + + return multistep.ActionContinue +} + +func (s *StepStopServer) Cleanup(state multistep.StateBag) {}